ORDER BY 觸發 Using filesort 是大表查詢性能殺手。MySQL 能利用索引順序直接返回有序結果,但需滿足嚴格的「索引列序、排序方向、WHERE 常量」三重約束。本文提煉 5 條鐵律,複盤真實優化案例,從 9 萬行全表掃描降至 3 千行索引範圍。
WHERE col = const,變量/子查詢失效ASC 或全部 DESC,混合方向失效SELECT * 需回表,但不影響排序優化| SQL 模式 | 索引 | 結果 | 原因 |
|---|---|---|---|
ORDER BY key1, key2 | INDEX(key1, key2) | ✅ | 順序匹配 |
WHERE key1=1 ORDER BY key1 DESC, key2 DESC | INDEX(key1, key2) | ✅ | WHERE 常量 + 方向統一 |
ORDER BY key1 DESC, key2 ASC | INDEX(key1, key2) | ❌ | 混合 ASC/DESC |
WHERE key2=1 ORDER BY key1 | INDEX(key1, key2) | ❌ | WHERE列非索引最左 |
WHERE key1=1 ORDER BY key1, key3 | INDEX(key1, key2, key3) | ❌ | 跳過 key2,不連續 |
WHERE key1=1 ORDER BY key2 | INDEX(key1, key2) | ✅ | WHERE最左常量 + ORDER BY次左 |
EXPLAIN SELECT ... ORDER BY create_date DESC LIMIT 10;
Using filesort → ❌ 文件排序,未用索引
SELECT ActionID, UserID, ...
FROM wikiuseractionlog
WHERE CreateDate > '0001-01-01 00:00:00'
AND ActionType IN (10,9,19,20)
ORDER BY CreateDate DESC
LIMIT 93060, 20;
EXPLAIN 结果:
type=ALL, key=NULL, rows=91859, Extra="Using where; Using filesort"
'0001-01-01' 導致選擇率極高(>20%),優化器放棄索引。改為 '2014-01-01',選擇率降至 4%,索引生效。
WHERE CreateDate > '2014-01-01 00:00:00'
-- rows: 91859 → 3737
CREATE INDEX idx_date_type ON wikiuseractionlog(CreateDate, ActionType);
-- 或 idx_type_date(ActionType, CreateDate) 視選擇率而定
-- 失效:ORDER BY CreateDate 但 WHERE 無 CreateDate
WHERE ActionType IN (...) ORDER BY CreateDate
-- 生效:WHERE 包含 CreateDate 常量範圍
WHERE CreateDate > '2014-01-01' ORDER BY CreateDate
原理:WHERE 範圍掃描時按索引順序讀取,天然有序,無需額外排序。
LIMIT 93060, 20 -- 掃描 9 萬行丟棄
-- 改用延遲關聯或游標分頁
WHERE CreateDate > '2014-01-01' AND CreateDate < 'last_seen_date' ORDER BY CreateDate LIMIT 20
-- IN 列表導致範圍查詢退化
WHERE ActionType IN (10,9,19,20)
-- 改為 UNION ALL 多個等值查詢,各自走索引
(SELECT ... WHERE ActionType=10 ORDER BY CreateDate LIMIT 20)
UNION ALL
(SELECT ... WHERE ActionType=9 ORDER BY CreateDate LIMIT 20)
...
ORDER BY CreateDate DESC LIMIT 20;
type=range, key=idx_date_type, key_len=8, rows=3737, Extra="Using where"
-- 無 Using filesort,性能提升 20 倍+
-- 表 a(id 普通列有索引)
SELECT * FROM a ORDER BY id; -- ❌ Using filesort
SELECT id FROM a ORDER BY id; -- ✅ Using index
SELECT * FROM a WHERE id=10 ORDER BY id; -- ✅ WHERE 引用 id
規律:ORDER BY 列必須出現在 SELECT 列表或 WHERE 條件中,才能觸發索引排序。
sort_buffer_size:排序緩衝區,避免臨時文件(會話級,建議 2-4MB)read_rnd_buffer_size:隨機讀緩衝,優化排序後回表tmpdir:指向高速存儲(NVMe/內存盤),緩解 filesort 磁碟壓力ORDER BY 優化不是玄學,是索引列序設計 + WHERE 常量約束 + 方向統一的工程規律。用 EXPLAIN 驗證每一次變更,讓「Using filesort」成為歷史。