首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL EXPLAIN 執行計劃完整解讀指南:欄位含義、優化判讀與實戰案例

     編輯:MySQL 資料庫相關知識

前言

EXPLAIN 是 MySQL 效能調校的「聽診器」。透過分析執行計劃,我們能洞悉優化器如何存取資料、使用何種索引、是否產生臨時表或檔案排序。本文系統解析 EXPLAIN 12 個輸出欄位含義、type 存取類型層級、Extra 關鍵提示、EXPLAIN ANALYZE (8.0.18+) 實際執行統計,並附實戰判讀邏輯與優化決策樹。

一、EXPLAIN 基本語法與變體


-- 傳統格式 (表格)
EXPLAIN SELECT * FROM users WHERE id = 1;

-- JSON 格式 (8.0+,含更多成本資訊)
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1;

-- 實際執行統計 (8.0.18+,真實耗時、回傳行數、迴圈次數)
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;

-- 追蹤優化器決策 (5.6+,需開啟 optimizer_trace)
SET SESSION optimizer_trace="enabled=on";
SELECT * FROM users WHERE id = 1;
SELECT * FROM information_schema.optimizer_trace;

二、12 個標準輸出欄位詳細解析

欄位含義關鍵判讀重點
idSELECT 識別符,查詢序列號相同 id 為同一查詢組;id 越大越先執行 (子查詢);id 相同按順序執行
select_type查詢類型SIMPLE (無 UNION/子查詢)、PRIMARY (最外層)、UNION (UNION 第 2+ 個)、DEPENDENT UNION、UNION RESULT、SUBQUERY、DEPENDENT SUBQUERY (相關子查詢)、DERIVED (衍生表/FROM 子查詢)、MATERIALIZED (物化子查詢)
table存取的表名 (或衍生表別名 )關聯查詢中顯示各表存取順序 (驅動表 → 被驅動表)
partitions匹配的分區 (分區表)NULL=非分區表;顯示具體分區名 = 分區剪枝生效
type存取類型 (核心指標,由優到劣)見下節詳細層級表
possible_keys可能用到的索引NULL = 無可用索引,**需檢查 WHERE/JOIN/ORDER BY 欄位是否有索引**
key實際使用的索引NULL = **全表掃描或未命中索引**,極度危險
key_len使用索引的位元組長度判斷複合索引用了幾欄:長度 = Σ(欄位長度) + NULL位(每欄1位) + 變長欄位長度資訊(2位)。越短越好 (精確性前提下)
ref索引查找的比較值來源const (常數)、col_name (關聯欄位)、func (函數結果)、NULL (無比較值)
rows預估掃描行數 (統計資訊推算)**越小越好**;與實際行數差距大 = 統計資訊過期需 ANALYZE;乘積 = 總預估成本
filtered預估符合條件行數百分比 (5.7+)rows × filtered/100 = 實際參與 JOIN/回傳行數;< 10% 表示條件過濾性差
Extra額外執行資訊 (關鍵優化線索)見下節關鍵值對照表

三、type 存取類型層級 (由優到劣)

類型描述典型場景效能評級
system表僅 1 行 (系統表)mysql.time_zone 等★★★★★
const主鍵/唯一鍵等值查詢,最多 1 行WHERE id=1 (id 為 PK/UK)★★★★★
eq_ref唯一索引等值 JOIN,每行匹配 1 行JOIN ON pk = pk★★★★★
ref非唯一索引等值查詢/JOINWHERE status=1 (status 有索引)★★★★☆
ref_or_nullref + 額外搜尋 NULLWHERE col=1 OR col IS NULL★★★★☆
index_merge索引合併優化 (多索引 UNION/INTERSECT)WHERE idx1=1 OR idx2=2★★★☆☆
unique_subqueryIN 子查詢轉等值查找 (主鍵)WHERE id IN (SELECT pk FROM t)★★★★☆
index_subqueryIN 子查詢轉索引查找 (非唯一)WHERE col IN (SELECT idx FROM t)★★★☆☆
range索引範圍掃描WHERE id > 100, BETWEEN, IN, LIKE 'abc%'★★★☆☆
index全索引掃描 (掃描整棵 B+Tree)SELECT indexed_col FROM t (覆蓋索引)★★☆☆☆
ALL全表掃描 (最差)無索引、索引失效、統計資訊錯誤★☆☆☆☆

四、Extra 關鍵值對照表 (優化紅綠燈)

Extra 值含義效能影響優化方向
Using index覆蓋索引:查詢欄位全在索引中,無需回表🟢 極佳設計覆蓋索引
Using whereServer 層過濾 (Storage Engine 返回後再篩選)🟡 正常下推條件至存儲引擎 (Index Condition Pushdown)
Using index conditionICP (Index Condition Pushdown):索引層過濾,減少回表🟢 優秀確保 5.6+ 開啟 ICP (預設 ON)
Using filesort檔案排序:無法用索引排序,需臨時表/記憶體排序🔴 **嚴重**調整索引順序滿足 ORDER BY、避免範圍查詢後排序、Limit 省略排序
Using temporary臨時表:GROUP BY/DISTINCT/UNION/ORDER BY 不同欄位需物化🔴 **嚴重**索引滿足 GROUP BY/ORDER BY、避免 SELECT *、子查詢改 JOIN
Using join bufferBlock Nested Loop JOIN (無索引關聯)🔴 嚴重加上 JOIN 條件索引、改用 Hash Join (8.0.18+)
Select tables optimized away聚合查詢優化:MIN/MAX/COUNT 直接讀索引極值🟢 極佳確保索引存在
Distinct找到第一匹配行後停止掃描🟡 正常-
Not existsLEFT JOIN 優化:找到匹配行後不再搜尋右表🟢 優秀-
Range checked for each record無理想索引,每行組合重新評估索引選擇🔴 極差建立複合索引覆蓋 WHERE 條件
Impossible WHEREWHERE 條件永假 (如 1=0)🟢 極佳-
Impossible HAVINGHAVING 條件永假🟢 極佳-

五、EXPLAIN ANALYZE 實戰優勢 (8.0.18+)


EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id=123 ORDER BY created_at DESC LIMIT 10;

-- 輸出關鍵實際值 (TREE 格式)
-> Limit: 10 row(s)  (actual time=0.5..1.2 rows=10 loops=1)
    -> Sort: orders.created_at DESC  (actual time=0.5..1.1 rows=1000 loops=1)
        -> Filter: (orders.user_id = 123)  (actual time=0.1..0.8 rows=1000 loops=1)
            -> Index range scan on orders using idx_user_id  (actual time=0.05..0.3 rows=1000 loops=1)
                (cost=100 rows=1000)
核心優勢:actual time (啟動/結束毫秒)、actual rows (實際行數)、loops (迴圈次數)、cost (優化器成本)。**直接對比 estimated rows vs actual rows 發現統計資訊偏差**。

六、實戰判讀邏輯與優化決策樹

步驟 1:看 type → 定性

  • ALL → **全表掃描**,必須加索引或改寫 SQL。
  • index → **全索引掃描**,若非覆蓋索引則需加 WHERE 條件限制範圍。
  • range/ref/eq_ref/const → **索引生效**,進入步驟 2。

步驟 2:看 key/key_len/ref → 定準

  • key=NULL → 索引未命中,檢查隱式轉型、函數包裹、最左前綴違反。
  • key_len 過短 → 複合索引未用全欄,檢查 WHERE/JOIN/ORDER BY 順序。
  • ref 為 func 或 NULL → 可能有函數/運算導致索引失效。

步驟 3:看 rows/filtered → 定量

  • rows 巨大 (>10萬) → 掃描成本高,需縮小範圍或分區。
  • filtered 低 (<10%) → 過濾性差,考慮複合索引包含過濾欄位。

步驟 4:看 Extra → 定紅綠燈

  • 🔴 Using filesort / Using temporary / Using join buffer → **立即優化**,參考對照表方向。
  • 🟢 Using index / Using index condition / Not exists → **良好模式**,保持。
  • 🟡 Using where → 常態,若配合 ref 且 rows 小可接受。

步驟 5:多表 JOIN 看 id/table 順序 → 定驅動表

  • id 相同、table 順序 = JOIN 順序 (驅動表 → 被驅動表)。
  • 驅動表應為**結果集最小、過濾性最強**的表 (通常有高選擇性 WHERE 條件)。
  • 被驅動表**必須有 JOIN 條件索引** (ref/eq_ref)。

七、常見誤區與避坑

誤區真相正確做法
EXPLAIN 顯示用了索引就一定快type=index (全索引掃描) 比 ALL 略好但仍慢;key_len 短未用全複合索引綜合判讀 type、key_len、rows、Extra
rows 是實際掃描行數rows 是**預估值** (統計資訊推算),實際行數見 EXPLAIN ANALYZE定期 ANALYZE TABLE 更新統計資訊
Using where 代表用了 WHERE 索引Using where 只是 Server 層過濾,不代表索引命中看 type、key、key_len 確認索引使用
複合索引順序隨意嚴格遵循最左前綴:等值 → 範圍 → 排序索引設計時同步規劃 WHERE/JOIN/ORDER BY
EXPLAIN 不會執行 SQLEXPLAIN ANALYZE **會真實執行** (含副作用),生產謹慎用生產用 EXPLAIN FORMAT=JSON 或備份庫跑 ANALYZE

八、總結:EXPLAIN 速查卡片


┌─────────────────────────────────────────────────────────────────┐
│ EXPLAIN 判讀三步走                                               │
├─────────────────────────────────────────────────────────────────┤
│ 1️⃣ type:  是否 ALL/index?  → 是 → 加索引/改寫                   │
│ 2️⃣ key:     是否 NULL?      → 是 → 檢查隱式轉型/函數/最左前綴   │
│ 3️⃣ Extra:   是否 🔴 filesort/temporary/join_buffer? → 是 → 優化 │
│         是否 🟢 Using index? → 否 → 嘗試覆蓋索引                 │
├─────────────────────────────────────────────────────────────────┤
│ 關鍵公式: 總成本 ≈ Σ(rows_i × filtered_i/100)                   │
│ 目標: type ≥ ref, key ≠ NULL, rows 最小, Extra 綠燈             │
└─────────────────────────────────────────────────────────────────┘
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved