ORDER BY 是查詢中最常見也最容易成為性能瓶頸的操作。MySQL 採用「索引排序優先,否則 Filesort」的雙軌策略。理解其原理、觸發條件、兩種 Filesort 算法、臨時表機制與 JOIN 排序模式,才能精準優化慢查詢。
ORDER BY 列與使用的索引鍵完全一致(或滿足最左前綴),且訪問類型為 range/ref/index → 直接按索引順序讀取,零排序開銷sort_buffer_size 內存中排序ORDER BY 與 GROUP BY 列不一致 → 先物質化結果集到臨時表再排序| 場景 | 原因 | 對策 |
|---|---|---|
| WHERE 用索引 A,ORDER BY 用索引 B | 單表單查詢只能用一個索引 | 建立複合索引覆蓋 WHERE+ORDER BY |
| 檢查行數過多且無覆蓋索引 | 優化器判斷全表掃描+排序更快 | 強制索引 / 優化 WHERE 條件 |
| ORDER BY 列混用 ASC/DESC | 索引無法同時滿足雙向 | 統一方向或建立反向索引 (8.0+) |
| ORDER BY 列使用表達式/函數 | 無法利用索引順序 | 生成列 / 函數索引 (8.0+) |
| WHERE 條件為範圍查詢,ORDER BY 滿足最左前綴但非等值 | 範圍列後的索引列失序 | 重設計索引順序 |
| LEFT JOIN 以右表列排序 | 驅動表非排序列 | 改用 INNER JOIN 或子查詢 |
| 算法 | 模式 | 特點 | 適用條件 |
|---|---|---|---|
| 雙路排序 (Two-pass) | <sort_key, rowid> | 先取排序鍵+行指針排序,再回表取數據 | MySQL 4.1 前唯一算法;max_length_for_sort_data 較小時 |
| 單路排序 (Single-pass) | 一次性取所有選中列 | 避免二次回表,佔用更多 sort_buffer | 4.1+ 改進算法;max_length_for_sort_data 較大時 |
選擇邏輯:所有返回列總長度 < max_length_for_sort_data → 單路;否則雙路。
max_length_for_sort_data:誘導優化器選單路排序,減少隨機 I/O(需確保內存充足)sort_buffer_size:減少分段排序、臨時表落盤(連接級參數,高並發下慎設過大,>2M 可能觸發 mmap() 效能下降)| 模式 | EXPLAIN Extra | 說明 |
|---|---|---|
| 索引有序輸出 | 無排序標記 | 驅動表索引天然有序,直接輸出 |
| 驅動表 Filesort | Using filesort | 先排序驅動表,再關聯 |
| 臨時表 Filesort | Using temporary; Using filesort | 結果集物質化後排序,開銷最大 |
Filesort 並不意味著「文件排序」。只要無法用索引獲得有序數據,就是 Filesort。它本質是快速排序,僅在
sort_buffer_size不足時分段排序並歸併,可能用到臨時文件。Filesort 與臨時表無直接關係。
EXPLAIN 觀察 Extra 欄:Using filesort / Using temporaryWHERE + ORDER BY + SELECT 列max_length_for_sort_data (預設 1024) 至 4096/8192SELECT 列表,避免 SELECT *WHERE id > last_id ORDER BY id LIMIT NSort_merge_passes 狀態變量,>0 表示發生分段歸併,需增大 sort_buffer