article:200 萬行,1.3 GBarticle_category:已建普通索引SELECT * FROM article WHERE article_category = 11 ORDER BY article_id DESC LIMIT 5問題:單值 = 查詢耗時 5 秒,說明索引未生效或回表代價大。
方案:建立**覆蓋索引** (Composite Index)
CREATE INDEX idx_cat_id ON article (article_category, article_id);
原理:
(category, id) 完美匹配 WHERE category=11 ORDER BY id DESC* 包含非索引列 → 仍需回表,但僅回表 5 行效果:0.0027 秒**,提速 **1800 倍**。
問題查詢:
-- IN 寫法
SELECT * FROM article WHERE article_category IN (2,3) ORDER BY article_id DESC LIMIT 5; -- 11.28s
-- OR 寫法
SELECT * FROM article WHERE article_category=2 OR article_category=3 ORDER BY article_id DESC LIMIT 5; -- 11.07s
根因:MySQL 優化器對 IN/OR **無法使用索引合併**,退化為**全表掃描 + filesort**。
(
SELECT * FROM article WHERE article_category = 2 ORDER BY article_id DESC LIMIT 5
)
UNION ALL
(
SELECT * FROM article WHERE article_category = 3 ORDER BY article_id DESC LIMIT 5
)
ORDER BY article_id DESC LIMIT 5;
效果:0.026 秒**,提速 **430 倍**。
| 寫法 | 執行計畫 | 索引利用 | 掃描行數 |
|---|---|---|---|
| IN (2,3) | type=ALL (全表掃描) | ❌ | 200 萬 |
| OR | type=ALL (全表掃描) | ❌ | 200 萬 |
| UNION ALL | type=ref (索引查找) × 2 | ✅ 覆蓋索引 | 5 + 5 = 10 |
WHERE 列 + ORDER BY 列 + SELECT 列(或僅回表極少行)LIMIT N 後再 UNION ALL 再 LIMIT N,確保總掃描行數可控UNION (去重) 會引入臨時表排序,**必須用 UNION ALL**Index Merge 優化,部分場景 IN/OR 可走索引合併,但覆蓋索引 + UNION ALL 仍是最穩方案
-- 原始:WHERE col IN (v1,v2,v3) ORDER BY pk DESC LIMIT N
-- 改寫:
(
SELECT * FROM tbl WHERE col = v1 ORDER BY pk DESC LIMIT N
)
UNION ALL
(
SELECT * FROM tbl WHERE col = v2 ORDER BY pk DESC LIMIT N
)
UNION ALL
(
SELECT * FROM tbl WHERE col = v3 ORDER BY pk DESC LIMIT N
)
ORDER BY pk DESC LIMIT N;
**「覆蓋索引 + UNION ALL 分支限制」** 是 MySQL 處理離散值多條件排序分頁的黃金組合。將 IN/OR 的全表掃描拆解為多次**索引精準定位 + 極少行回表**,從秒級降至毫秒級,且邏輯等價、零業務侵入。