在 EXPLAIN 輸出中出現 Using filesort 往往意味著查詢性能隱患。本文深入剖析 MySQL 兩種排序實現方式、filesort 觸發場景、雙路/單路排序算法差異,並給出參數調優與 SQL 改寫的實戰優化指南。
| 方式 | EXPLAIN 顯示 | 核心原理 |
|---|---|---|
| 有序索引掃描 | Using index | 直接按索引順序讀取,無需額外排序 |
| 文件排序 | Using filesort | 在內存/磁盤臨時表中對結果集排序 |
⚠️ 關鍵限制:MySQL 單表查詢只能使用一個索引。若 WHERE 已用上索引,ORDER BY 通常無法再利用索引,從而觸發 filesort。
-- 表 test 有聯合索引 idx_rdate (rdate, inventid, customerid)
EXPLAIN SELECT inventid FROM test WHERE rdate='2011-12-14' ORDER BY inventid, customerid;
-- Using index ✅
EXPLAIN SELECT * FROM test WHERE rdate='2011-12-14' ORDER BY inventid, customerid;
-- Using filesort ❌ (SELECT * 不在索引覆蓋範圍)
(rdate, inventid) → ORDER BY rdate, inventid ✅ORDER BY inventid, rdate ❌ 順序不符ORDER BY rdate ASC, inventid DESC ❌ 混合 ASC/DESCWHERE rdate='2011-12-16' ORDER BY inventid; -- 索引 (rdate, inventid) ✅
WHERE rdate > '2011-12-16' ORDER BY inventid; -- 範圍查詢破壞最左前綴 ❌ (Using filesort)
ORDER BY 非驅動表列WHERE/ORDER BY 列使用函數/表達式(如 ORDER BY UPPER(name))LEFT JOIN 時按右表列排序| 算法 | 流程 | 特點 | 適用條件 |
|---|---|---|---|
| 雙路排序 (MySQL 4.1 前) | 1. 讀排序鍵+行指針 → sort_buffer 排序 2. 按指針回表讀其餘列 | 兩次訪問表,I/O 多 | max_length_for_sort_data < 總返回列長度 |
| 單路排序 (MySQL 4.1+) | 一次讀取所有需返回列 → sort_buffer 排序 → 直接輸出 | 一次 I/O,佔用更多 sort_buffer | max_length_for_sort_data ≥ 總返回列長度 |
MySQL 通過比較 max_length_for_sort_data 與查詢返回列總長度自動選擇算法。
[mysqld]
max_length_for_sort_data = 4096 # 默認 1024,適當增大
案例:某同事 DB 大量排序等待,調大此參數後負載驟降、響應提速。
避免 SELECT *,只取業務必需列,使總長度落入 max_length_for_sort_data 範圍,自然觸發單路排序。
[mysqld]
sort_buffer_size = 4M # 每線程獨享,視內存與併發調整
排序數據超過 sort_buffer_size 會分段寫磁盤臨時表(多路合併排序),劇增 I/O。增大該值可減少分段次數。
WHERE + ORDER BY 組合建立聯合索引,順序遵循「等值條件列 → 範圍條件列 → 排序列」。SELECT 列,實現 Using index 雙重收益。filesort 非洪水猛獸,但預示著額外 CPU/IO 開銷。優化優先級:索引覆蓋消除排序 > 參數調優單路排序 > 精簡列減少排序成本。配合 EXPLAIN 觀察 Extra 列,針對性調優,事半功倍。