ORDER BY 排序與 LIMIT 分頁是 Web 應用最常用的查詢模式。隨著資料量增長,「深分頁」 (LIMIT 1000000, 20) 成為效能殺手。本文系統講解排序索引匹配規則、分頁優化三大技巧 (延遲關聯、游標分頁、覆蓋索引)、MySQL 8.0 降序索引新特效,並給出效能對比實測。
ORDER BY 列順序、方向 (ASC/DESC) **完全匹配** 索引順序 (MySQL 8.0+ 支援混合方向索引)。WHERE 條件為索引最左前綴等值查詢,ORDER BY 使用後續列。WHERE 時,ORDER BY 必須匹配索引最左前綴。| 索引 | 查詢 | 是否用索引排序 | Extra |
|---|---|---|---|
INDEX (a, b) | WHERE a=1 ORDER BY b | ✅ | Using index condition |
INDEX (a, b) | WHERE a>1 ORDER BY b | ❌ (範圍查詢後列不可用) | Using filesort |
INDEX (a, b) | ORDER BY a, b | ✅ | Using index |
INDEX (a, b) | ORDER BY b, a | ❌ | Using filesort |
INDEX (a, b) | ORDER BY a ASC, b DESC | MySQL 5.7 ❌ / 8.0 ✅ (降序索引) | - |
sort_buffer_size (預設 256KB) 記憶體排序,超出則建磁碟臨時表 (Using temporary; Using filesort)。sort_buffer。SELECT * FROM article ORDER BY id LIMIT 1000000, 20;
**執行流程**:掃描索引/表 → 排序 (或索引順序讀取) → **跳過 100 萬行** → 回傳 20 行。
**代價**:隨 OFFSET 增大線性增長,IOPS、CPU 雙高。
-- 原始 (慢)
SELECT * FROM article ORDER BY id LIMIT 1000000, 20;
-- 優化 (快):內層只掃描主鍵索引 (Covering),外層回表 20 行
SELECT a.* FROM article a
INNER JOIN (
SELECT id FROM article ORDER BY id LIMIT 1000000, 20
) b USING (id);
**原理**:內層 type=index (Full Index Scan),無回表;外層 type=eq_ref 精準回表 20 行。
-- 首頁 SELECT * FROM article WHERE id > 0 ORDER BY id LIMIT 20; -- 後續頁:記住上一頁最後一行的 id SELECT * FROM article WHERE id > 1000020 ORDER BY id LIMIT 20;
**優勢**:無 OFFSET,索引範圍查詢 type=range,效能恆定 O(1)。
**適用**:無需跳頁、按時間/ID 遞增瀏覽 (Feed、日誌、訊息列表)。
-- 建立覆蓋索引 ALTER TABLE article ADD INDEX idx_created_id (created_at, id); -- 查詢只需 id, title, created_at SELECT id, title, created_at FROM article WHERE created_at >= '2024-01-01' ORDER BY created_at, id LIMIT 20;
EXPLAIN 顯示 Using index,全程不回表,極快。
-- 建立混合方向索引 ALTER TABLE article ADD INDEX idx_status_created (status, created_at DESC); -- 查詢:狀態=1,按建立時間倒序 SELECT * FROM article WHERE status = 1 ORDER BY created_at DESC LIMIT 20; -- 8.0 可直接用索引排序,5.7 需 filesort
| 方案 | LIMIT 10,20 | LIMIT 100000,20 | LIMIT 1000000,20 |
|---|---|---|---|
原始 SELECT * ... LIMIT offset,20 | 2 ms | 120 ms | 1,800 ms |
| 延遲關聯 | 3 ms | 15 ms | 25 ms |
| 游標分頁 | 2 ms | 2 ms | 2 ms |
| 覆蓋索引 (只查 3 欄) | 1 ms | 1 ms | 1 ms |
需要跳頁 (頁碼導航)?
├─ 是 → OFFSET 分頁
│ ├─ 資料量 < 10萬 → 直接 LIMIT offset, size
│ ├─ 資料量 10萬~100萬 → 延遲關聯 + 覆蓋索引
│ └─ 資料量 > 100萬 → 禁止深分頁,改用「搜尋+篩選」縮小結果集
└─ 否 (無限滾動、Feed) → 游標分頁 (WHERE id > last_id)
└─ 必須支援排序切換 → 為每種排序建立對應覆蓋索引
結語: 分頁優化的核心是**減少掃描與回表行數**。掌握「延遲關聯處理深 OFFSET」、「游標分頁消除 OFFSET」、「覆蓋索引消除回表」、「降序索引支援混合排序」四大技巧,配合 EXPLAIN 驗證 Using index/Using index condition,即可從容應對億級資料分頁。