EXPLAIN 是 MySQL 效能調優的「第一把手術刀」,超過 80% 的慢查詢問題可透過它定位根因。本文系統講解各欄位含義、關鍵指標判讀、常見問題模式與優化實戰技巧。
EXPLAIN SELECT ... FROM ... [WHERE ...];
-- MySQL 5.6+ 支援格式化輸出
EXPLAIN FORMAT=JSON SELECT ...;
EXPLAIN ANALYZE SELECT ...; -- 8.0.18+ 實際執行統計
| 欄位 | 含義 | 關鍵判讀重點 |
|---|---|---|
id | SELECT 查詢序號 | 相同 id 由上往下執行;id 越大優先級越高(子查詢) |
select_type | 查詢類型 | SIMPLE(無子查詢/UNION)、PRIMARY(最外層)、UNION、DEPENDENT SUBQUERY(相關子查詢,效能殺手) |
table | 存取的表名/衍生表 | <derived2> 表示衍生表(子查詢物化) |
partitions | 匹配的分區 | 分區表才顯示,檢查分區裁剪是否生效 |
type ⭐ | 存取類型(效能核心指標) | 由好到壞:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL至少達 range,最佳達 ref/eq_ref |
possible_keys | 可能用到的索引 | 為 NULL 表示無可用索引,需建立 |
key ⭐ | 實際使用的索引 | 為 NULL 表示全表掃描;與 possible_keys 對比驗證索引選擇 |
key_len | 索引長度(位元組) | 越短越好(不失精度前提下);多欄索引可判斷用到第幾欄 |
ref | 與 key 配合的比較值 | const(常數)、欄位名(關聯查詢)、func(函數結果) |
rows ⭐ | 預估掃描行數 | 越小越好;若與實際相差巨大,執行 ANALYZE TABLE 更新統計資訊 |
filtered | 按條件過濾後剩餘行百分比 | 越接近 100 越好;過低表示索引選擇性差 |
Extra ⭐ | 額外資訊(優化關鍵) | 見下表關鍵值 |
| 值 | 含義 | 處理建議 |
|---|---|---|
Using index | 覆蓋索引,僅掃描索引樹 | ✅ 極佳,無需回表 |
Using where | Server 層過濾 | 正常;若 type=ALL 且無 Using index,需加索引 |
Using temporary | 建立臨時表(通常 GROUP BY/ORDER BY) | ⚠️ 優化:加索引消除排序、避免 SELECT * |
Using filesort | 無法用索引排序,需額外排序 | ⚠️ 優化:調整索引順序覆蓋 ORDER BY |
Using index condition | ICP(Index Condition Pushdown) | ✅ 5.6+ 優化,存儲引擎層過濾 |
Range checked for each record | 無好索引,逐行檢查可用索引 | ❌ 極差,需建立合適索引 |
Select tables optimized away | 聚合函數直接走索引定位 | ✅ MIN/MAX 等優化 |
Impossible WHERE | WHERE 條件永不成立 | 檢查條件邏輯 |
Distinct | 找到第一匹配即停止 | 正常優化 |
FORCE INDEX 或重寫查詢、調整索引順序。EXISTS。-- JSON 格式含更多優化器決策細節
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE user_id=100;
-- 8.0.18+ 實際執行統計(含耗時、實際掃描行數)
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id=100;
type=index 並非好事,代表**全索引掃描**,若伴隨 Using index 則為覆蓋索引掃描。ref 不代表「引用」,而是「參考值類型」。rows 是**預估值**,非實際值,統計資訊過時會誤導優化器。黃金法則:先 EXPLAIN,再建索引,最後驗證。盲目加索引反而拖慢寫入效能。