**優化目標:盡可能讓 ORDER BY 走索引掃描。**
否則退化為 Filesort。
CREATE TABLE actor( actor_id INT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(16) NOT NULL DEFAULT '', password VARCHAR(16) NOT NULL DEFAULT '', PRIMARY KEY (actor_id), KEY (name) ) ENGINE=InnoDB;
| SQL | EXPLAIN type | key | Extra | 結果 |
|---|---|---|---|---|
ORDER BY actor_id | index | PRIMARY | Using index | ✅ 走主鍵索引 |
ORDER BY password | ALL | NULL | Using filesort | ❌ 無索引,全表掃描+排序 |
ORDER BY name | index | name | Using index | ✅ 走輔助索引 |
SELECT * FROM t WHERE columnX = value ORDER BY sort_col -- 最優索引:(columnX, sort_col)
等值條件列在前,排序列在後,利用索引過濾→排序一氣呵成。
SELECT * FROM t WHERE columnX IN (v1,v2) ORDER BY sort_col -- 無法利用索引排序,因 columnX 非單值,索引順序被打亂
SELECT * FROM t WHERE uid=1 ORDER BY x, y LIMIT 10 -- 最優索引:(uid, x, y) >> (x, y, uid) -- 驅動表過濾列優先,排序列緊隨其後
| 場景 | 示例 | 原因 |
|---|---|---|
| 不同索引鍵混合排序 | ORDER BY key1, key2(分別建單列索引) | 無法合併兩棵 B+ 樹順序 |
| 非連續索引鍵部分排序 | 聯合索引 (a,b),WHERE c=1 ORDER BY b | 缺少引導列 a |
| ASC/DESC 混用 | 聯合索引 (a,b),ORDER BY a DESC, b ASC | 索引單向存儲,無法滿足混合方向 |
| WHERE 與 ORDER BY 列不同 | WHERE key2=1 ORDER BY key1 | 索引無法同時服務過濾與排序 |
| 排序列使用函數/表達式 | ORDER BY YEAR(logindate) | 索引存儲原始值,函數破壞順序 |
| 算法 | MySQL 版本 | 流程 | 優缺點 |
|---|---|---|---|
| 兩遍掃描 | ≤ 4.1 | 1. 取排序鍵+行指針 → 排序 2. 按指針回表取列 | 內存小,但雙倍 I/O(隨機回表) |
| 單遍掃描 | ≥ 4.1 | 一次性取所有需求列 → 排序 → 輸出 | I/O 少,但內存大;大列會爆 sort_buffer |
控制參數:max_length_for_sort_data。若取出列總長超過此值,退回兩遍算法。**優化建議:SELECT 只取必要列,避免 SELECT * 觸發兩遍算法。**
Using filesort:ORDER BY 僅引用驅動表列 → 先排序驅動表,再 JOIN。Using temporary; Using filesort:ORDER BY 引用非驅動表列 → 必須先 JOIN 生成臨時表,再排序,**性能殺手**。SHOW INDEX 確認列順序與 ORDER BY 一致。Using index,避免回表。ORDER BY idx_col LIMIT 10 配合索引,僅掃描 10 行。ORDER BY create_time 而非 ORDER BY DATE(create_time)。**索引不僅加速查找,更是排序的免費加速器。** 設計索引時將「過濾列」與「排序列」組合,順序為 等值條件列 → 範圍條件列 → 排序列,方向保持一致。配合 EXPLAIN 觀察 type=index 和 Extra=Using index,即可確認排序已走索引。若無法避免 Filesort,通過只選必要列、調大 sort_buffer、加大 max_length_for_sort_data 將損失降至最低。