不僅 ORDER BY,GROUP BY、DISTINCT、UNION 等都隱含排序操作。
假設索引 KEY k1(a,b), KEY k2(c)
SELECT * FROM t ORDER BY a, b;
SELECT * FROM t WHERE a = 1 ORDER BY b;
SELECT * FROM t WHERE a > 1 ORDER BY a ASC;
SELECT * FROM t WHERE a = 1 AND b > 2 ORDER BY b;
| SQL | 原因 |
|---|---|
ORDER BY a, b, c | 跨多個索引 |
ORDER BY b, a | 順序與索引列順序不一致 |
ORDER BY a DESC, b ASC | 升降序混合(8.0+ 支援降序索引) |
WHERE a > 1 ORDER BY b | 范圍查詢列後的列無序 |
執行計劃出現 Using filesort 觸發,不一定落盤,取決於 sort_buffer_size 與結果集大小。
(主鍵, 排序列) 進 sort_buffer(pk, sort_key) → 回表取 SELECT 列(隨機 I/O)read_rnd_buffer_size 內對 pk 排序,轉順序 I/O(SELECT 列...) 進 sort_buffermax_length_for_sort_data 限制(元組長度超限強制退回常規)ORDER BY ... LIMIT M,NO(M+N)LIMIT 10 OFFSET 1000000 這類大偏移排序開銷
CREATE TABLE t1(id INT PK, c1 INT, c2 VARCHAR(128));
INSERT ... (c1=2 有 3 條記錄)
-- 第 1 頁
SELECT * FROM t1 ORDER BY c1 LIMIT 0,3;
-- 第 2 頁
SELECT * FROM t1 ORDER BY c1 LIMIT 3,3;
現象: id=4 的行同時出現在兩頁(5.6 優先隊列 + 不穩定排序導致)。
修復: ORDER BY c1, id 加入唯一列保證穩定性。
SELECT id,status,c1,c2 FROM t2 FORCE INDEX(c1) WHERE c1>='b' ORDER BY status;
SELECT id,status FROM t2 FORCE INDEX(c1) WHERE c1>='b' ORDER BY status;
根因: 前者元組超長觸發常規排序(回表前 pk 排序改變相對序),後者觸發優化排序(保持排序後順序)。
修復: 調大 max_length_for_sort_data 統一走優化排序,或在 ORDER BY 加唯一列。
需求:按指定 ID 順序 (2,3,5,4) 優先,其餘自然序
SELECT * FROM `user` ORDER BY FIELD(id, 2,3,5,4) ASC;
-- 結果順序:2,3,5,4,1,6...
SELECT * FROM `user` ORDER BY FIELD(id, 2,3,5,4) DESC;
-- 結果順序:1,6...,4,5,3,2
-- INSTR(str, substr) 返回 substr 在 str 中首次位置
SELECT * FROM `user` ORDER BY INSTR('2,3,5,4', id) DESC;
-- 位置越大(越靠後)排越前 → 4,5,3,2,1,6...
-- LOCATE(substr, str) 參數順序相反
SELECT * FROM `user` ORDER BY LOCATE(id, '2,3,5,4') DESC;
-- 同 INSTR 效果
-- 目標順序 (2,3,5,4) 置頂,其餘按 id 升序
SELECT * FROM `user`
ORDER BY
CASE WHEN id IN (2,3,5,4) THEN 0 ELSE 1 END, -- 分組優先級
FIELD(id, 2,3,5,4), -- 組內指定序
id; -- 其餘自然序
| 參數 | 建議 |
|---|---|
| sort_buffer_size | 會話級,256K-2M;大查詢臨時調大,避免全局設大 |
| max_length_for_sort_data | 1024-4096;優化排序與常規排序分水嶺 |
| read_rnd_buffer_size | 256K-1M;常規排序回表前 pk 排序緩存 |
LIMIT M,N 必加唯一列保證分頁穩定
4. 自定義排序用 FIELD() 或 CASE WHEN 組合
5. 參數調優以會話級動態調整為主,全局保守