排序是數據庫核心功能,ORDER BY、GROUP BY、DISTINCT 均隱含排序。本文涵蓋:索引避免排序條件、Filesort 內部算法演進、關鍵參數、排序一致性陷阱案例。
假設表 t1 有索引 key1(key_part1, key_part2)、key2(key2)。
| 可利用索引排序 | 無法利用索引排序 |
|---|---|
ORDER BY key_part1, key_part2 | ORDER BY key_part1, key_part2, key2(跨多索引) |
WHERE key_part1=const ORDER BY key_part2 | ORDER BY key_part2, key_part1(順序不匹配) |
WHERE key_part1>const ORDER BY key_part1 ASC | ORDER BY key_part1 DESC, key_part2 ASC(升降序混用) |
WHERE key_part1=const1 AND key_part2>const2 ORDER BY key_part2 | WHERE key_part1>const ORDER BY key_part2(範圍查詢導致後列失效) |
核心原則:索引列順序、排序方向、WHERE 條件類型需與索引定義嚴格對齊。
測試表:CREATE TABLE t1(id INT PRIMARY KEY, col1 VARCHAR(64), col2 VARCHAR(64), col3 VARCHAR(64), KEY(col1,col2));
SQL:SELECT col1,col2,col3 FROM t1 WHERE col1>100 ORDER BY col2;
(id, col2) 放入 sort_buffer。(id, col2),回表撈取 (col1,col2,col3)(隨機 IO)。read_rnd_buffer 中對 ID 排序,將隨機 IO 轉順序 IO。(col1,col2,col3) 全字段放入 sort_buffer,排序後直接返回,省去回表。sort_buffer 容納行數減少,易觸發臨時文件歸並。max_length_for_sort_data 控制:元組長度超過該值強制退回常規排序。MySQL 5.6+ 針對 ORDER BY ... LIMIT M, N 引入,基於堆排序:
sort_buffer 壓力,小 LIMIT 場景幾乎不產生臨時文件。表 t1(id PK, c1 INT, c2 VARCHAR),c1 有 3 行值為 2。
ORDER BY c1 LIMIT 0,3 與 LIMIT 3,3 結果中 id=4 重複出現。
原因:5.6 引入優先隊列排序(堆排序),堆排序非穩定,相同 c1 值的行相對順序不固定。
修復:ORDER BY c1, id 引入唯一主鍵保證排序鍵唯一。
表 t2(id PK, status INT, c1 VARCHAR(255) KEY, c2, c3),c1 索引順序對應 status 為 2,3,2,4。
SQL1:SELECT id,status,c1,c2 ... ORDER BY status → status=2 的兩行順序為 id=5,6。
SQL2:SELECT id,status ... ORDER BY status → 順序為 id=6,5。
原因:SQL1 返回列長度超過 max_length_for_sort_data,使用常規排序,回表前對 ID 排序導致順序變化;SQL2 使用優化排序,保持索引掃描原始相對順序。
修復:增大 max_length_for_sort_data(如 2048)強制優化排序,或在 ORDER BY 加唯一列。
| 參數 | 作用 | 建議 |
|---|---|---|
sort_buffer_size | 排序緩存大小(會話級) | 256K-2M,避免過大導致 OOM |
max_length_for_sort_data | 優化排序元組長度閾值 | 1024-4096,視業務字段長度調整 |
read_rnd_buffer_size | 回表前 ID 排序緩存 | 256K-1M,配合常規排序優化隨機 IO |