MySQL EXPLAIN 關(guān)鍵參數(shù)實(shí)戰(zhàn)舉例
一、前言
1.1 什么是EXPLAIN
EXPLAIN是MySQL提供的SQL執(zhí)行計(jì)劃分析命令,用于展示MySQL優(yōu)化器如何執(zhí)行SQL語句。通過EXPLAIN可以分析索引使用情況、表連接順序、掃描行數(shù)等關(guān)鍵信息,是SQL性能優(yōu)化的核心工具。
1.2 基礎(chǔ)知識(shí)要求
- SQL基礎(chǔ):了解基本的SELECT、JOIN語法
- 索引概念:熟悉單列索引、聯(lián)合索引、覆蓋索引
- 執(zhí)行計(jì)劃:理解MySQL優(yōu)化器的工作方式
二、EXPLAIN的使用方法
2.1 基本語法
-- 分析SELECT語句 EXPLAIN SELECT * FROM users WHERE id = 1; -- 分析DELETE/UPDATE/INSERT(MySQL 5.6+) EXPLAIN DELETE FROM users WHERE status = 'inactive'; -- 顯示更詳細(xì)信息(MySQL 8.0.18+) EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1; -- 實(shí)際執(zhí)行并顯示耗時(shí) -- 輸出JSON格式(便于程序解析) EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1; -- 查看連接中正在執(zhí)行的SQL的執(zhí)行計(jì)劃 EXPLAIN FOR CONNECTION 123; -- 123為connection_id
2.2 EXPLAIN輸出字段概覽
EXPLAIN SELECT o.order_id, u.user_name FROM orders o LEFT JOIN users u ON o.user_id = u.user_id WHERE o.order_status = 'pending' AND o.created_at >= '2024-01-01' LIMIT 10\G
輸出結(jié)果示例:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: o
partitions: NULL
type: range
possible_keys: idx_status_created,idx_created_at
key: idx_status_created
key_len: 102
ref: NULL
rows: 185000
filtered: 100.00
Extra: Using index condition
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: u
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: test.o.user_id
rows: 1
filtered: 100.00
Extra: NULL三、核心參數(shù)詳解
3.1 id:查詢標(biāo)識(shí)符
| 值 | 含義 | 示例場(chǎng)景 |
|---|---|---|
| 相同id | 順序執(zhí)行,從上到下 | 多表JOIN |
| 不同id | id越大越先執(zhí)行(優(yōu)先級(jí)更高) | 子查詢、UNION |
示例:
-- id相同:多表JOIN EXPLAIN SELECT * FROM orders o, users u WHERE o.user_id = u.user_id; -- 結(jié)果:兩個(gè)id均為1 -- id不同:子查詢 EXPLAIN SELECT * FROM users WHERE user_id IN (SELECT user_id FROM orders); -- 結(jié)果:子查詢id=2,外層查詢id=1(子查詢先執(zhí)行)
3.2 select_type:查詢類型
| 類型 | 含義 | 優(yōu)化關(guān)注點(diǎn) |
|---|---|---|
| SIMPLE | 簡(jiǎn)單查詢,不包含子查詢或UNION | 最理想,無額外開銷 |
| PRIMARY | 最外層查詢 | 優(yōu)化重點(diǎn) |
| SUBQUERY | 子查詢(非FROM子句) | 盡量減少子查詢,可改寫為JOIN |
| DERIVED | 派生表(FROM子句中的子查詢) | 會(huì)生成臨時(shí)表,注意性能 |
| UNION | UNION中的第二個(gè)或后續(xù)查詢 | 每個(gè)UNION分支單獨(dú)分析 |
| UNION RESULT | UNION的結(jié)果集 | 合并結(jié)果的開銷 |
| DEPENDENT SUBQUERY | 依賴外部查詢的子查詢 | ?? 高危,每行外層數(shù)據(jù)都會(huì)執(zhí)行一次 |
重點(diǎn)關(guān)注:
-- 避免DEPENDENT SUBQUERY(子查詢依賴外層) EXPLAIN SELECT * FROM users u WHERE (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.user_id) > 5; -- 優(yōu)化:改為JOIN + GROUP BY -- 注意DERIVED臨時(shí)表開銷 EXPLAIN SELECT * FROM (SELECT * FROM orders WHERE status='pending') t; -- 建議:直接查詢,或創(chuàng)建視圖
3.3 type:訪問類型(最重要指標(biāo)之一)
性能從優(yōu)到劣排序:
system > const > eq_ref > ref > range > index > ALL
| 類型 | 說明 | 示例 | 優(yōu)化目標(biāo) |
|---|---|---|---|
| system | 系統(tǒng)表,只有一行數(shù)據(jù) | 極少出現(xiàn) | ? 最優(yōu) |
| const | 主鍵或唯一索引等值查詢,最多返回一行 | WHERE id = 1 | ? 理想 |
| eq_ref | 連接查詢時(shí),被驅(qū)動(dòng)表使用主鍵或唯一索引 | JOIN + 主鍵關(guān)聯(lián) | ? 優(yōu)秀 |
| ref | 使用非唯一索引等值查詢 | WHERE status = 'pending' | ? 良好 |
| range | 索引范圍掃描 | WHERE id > 100、BETWEEN、IN | ? 可接受 |
| index | 全索引掃描(掃描整個(gè)索引樹) | 覆蓋索引但無WHERE條件 | ?? 需優(yōu)化 |
| ALL | 全表掃描 | 無索引或索引失效 | ? 必須優(yōu)化 |
實(shí)戰(zhàn)對(duì)比:
-- const:最優(yōu) EXPLAIN SELECT * FROM users WHERE user_id = 1; -- type=const -- ref:良好 EXPLAIN SELECT * FROM orders WHERE order_status = 'pending'; -- type=ref(假設(shè)status有索引) -- range:可接受 EXPLAIN SELECT * FROM orders WHERE created_at >= '2024-01-01'; -- type=range(假設(shè)created_at有索引) -- ALL:必須優(yōu)化 EXPLAIN SELECT * FROM orders WHERE order_status = 'pending'; -- type=ALL(status無索引)
3.4 possible_keys:可能使用的索引
| 值 | 含義 | 處理建議 |
|---|---|---|
| 有索引名 | 優(yōu)化器可能使用這些索引 | 正常 |
| NULL | 沒有可用的索引 | 考慮添加索引 |
注意:possible_keys有值不代表實(shí)際使用,需結(jié)合key字段判斷。
3.5 key:實(shí)際使用的索引
| 值 | 含義 | 優(yōu)化建議 |
|---|---|---|
| 有索引名 | 實(shí)際使用了該索引 | 良好 |
| NULL | 未使用索引(全表掃描或全索引掃描) | 需要優(yōu)化 |
| PRIMARY | 使用了主鍵索引 | 最優(yōu) |
關(guān)鍵場(chǎng)景:
-- possible_keys有值但key為NULL:索引未被使用 EXPLAIN SELECT * FROM orders WHERE DATE(created_at) = '2024-01-01'; -- possible_keys: idx_created_at, key: NULL -- 原因:對(duì)索引字段使用了函數(shù),索引失效 -- 實(shí)際使用了聯(lián)合索引 EXPLAIN SELECT * FROM orders WHERE order_status = 'pending' AND created_at > '2024-01-01'; -- key: idx_status_created(聯(lián)合索引)
3.6 key_len:使用的索引字節(jié)長(zhǎng)度
作用:
- 判斷聯(lián)合索引中實(shí)際使用了哪些字段
- 長(zhǎng)度越長(zhǎng),表示使用的索引字段越多
計(jì)算規(guī)則(以InnoDB為例):
| 數(shù)據(jù)類型 | 長(zhǎng)度計(jì)算 |
|---|---|
TINYINT | 1字節(jié) |
INT | 4字節(jié) |
BIGINT | 8字節(jié) |
VARCHAR(100) | 100*3 + 2(UTF8mb4)或 100 + 2(Latin1) |
CHAR(10) | 10*字符集字節(jié)數(shù) |
| 允許NULL | 額外+1字節(jié) |
實(shí)戰(zhàn)分析:
-- 聯(lián)合索引:idx_status_created (order_status VARCHAR(20), created_at DATETIME) SHOW INDEX FROM orders; -- 查看索引定義 EXPLAIN SELECT * FROM orders WHERE order_status = 'pending' AND created_at >= '2024-01-01'; -- key_len = 83 -- 計(jì)算:order_status(20*3+2=62) + created_at(8) + NULL標(biāo)志(1) = 71? 實(shí)際83包含額外開銷 -- 如果只使用第一個(gè)字段 EXPLAIN SELECT * FROM orders WHERE order_status = 'pending'; -- key_len = 62(僅使用了order_status字段)
3.7 ref:索引列的比較對(duì)象
| 值 | 含義 | 示例 |
|---|---|---|
| const | 與常量比較 | WHERE id = 1 |
| 表名.字段 | 與其他表字段比較 | JOIN條件 |
| NULL | 非等值查詢或未使用索引 | WHERE id > 10 |
示例:
EXPLAIN SELECT * FROM orders o, users u WHERE o.user_id = u.user_id AND o.order_status = 'pending'; -- table=o 的 ref: const (order_status='pending') -- table=u 的 ref: test.o.user_id (關(guān)聯(lián)到orders表的user_id)
3.8 rows:預(yù)估掃描行數(shù)
含義:MySQL優(yōu)化器預(yù)估需要讀取的行數(shù)(非精確值)
重要性:
- 核心優(yōu)化指標(biāo),與查詢耗時(shí)正相關(guān)
- 目標(biāo):
rows盡可能小 - 若
rows接近表總行數(shù),說明索引效果差
實(shí)戰(zhàn):
-- 全表掃描:rows ≈ 表總行數(shù) EXPLAIN SELECT * FROM orders WHERE order_status = 'pending'; -- rows: 5,000,000(全表) -- 添加索引后:rows大幅下降 EXPLAIN SELECT * FROM orders WHERE order_status = 'pending'; -- rows: 250,000(索引過濾后)
3.9 filtered:過濾后剩余行數(shù)百分比
含義:滿足WHERE條件的行數(shù)占rows的預(yù)估百分比
計(jì)算:實(shí)際返回行數(shù) ≈ rows × filtered%
重要性:
filtered越低,說明WHERE條件過濾效果好- 低
filtered但rows大時(shí),需要更精準(zhǔn)的索引
示例:
EXPLAIN SELECT * FROM orders WHERE order_status = 'pending' AND user_id = 100; -- rows: 250000, filtered: 1.00 -- 實(shí)際返回行數(shù) ≈ 2500(過濾掉了99%的數(shù)據(jù)) -- 優(yōu)化:創(chuàng)建(status, user_id)聯(lián)合索引 -- 優(yōu)化后 rows: 50, filtered: 100.00
3.10 Extra:額外信息(重要優(yōu)化線索)
| 值 | 含義 | 優(yōu)化建議 |
|---|---|---|
| Using index | 覆蓋索引,不需要回表 | ? 最優(yōu)狀態(tài) |
| Using index condition | 索引下推(ICP) | ? 良好,MySQL 5.6+優(yōu)化 |
| Using where | 使用WHERE過濾(在Server層) | ?? 可接受,但索引層過濾更優(yōu) |
| Using filesort | 需要額外排序,無法利用索引 | ? 需優(yōu)化,添加排序字段索引 |
| Using temporary | 使用臨時(shí)表(GROUP BY/UNION/DISTINCT) | ? 需優(yōu)化,通常是性能殺手 |
| Using join buffer | 連接緩沖區(qū)(Block Nested Loop) | ?? 被驅(qū)動(dòng)表缺少索引 |
| Using index for group-by | 使用索引優(yōu)化GROUP BY | ? 良好 |
| Impossible WHERE | WHERE條件永遠(yuǎn)為假 | 檢查業(yè)務(wù)邏輯 |
| No tables used | 沒有FROM子句或FROM DUAL | 正常 |
重點(diǎn)優(yōu)化場(chǎng)景:
場(chǎng)景1:Using filesort
-- 問題SQL EXPLAIN SELECT * FROM orders WHERE status='pending' ORDER BY created_at DESC; -- Extra: Using where; Using filesort -- 優(yōu)化:添加(status, created_at)聯(lián)合索引 CREATE INDEX idx_status_created ON orders(status, created_at); -- 優(yōu)化后Extra不再出現(xiàn)filesort
場(chǎng)景2:Using temporary
-- 問題SQL EXPLAIN SELECT status, COUNT(*) FROM orders GROUP BY status ORDER BY COUNT(*); -- Extra: Using temporary; Using filesort -- 優(yōu)化:拆分為兩個(gè)查詢,或調(diào)整GROUP BY/ORDER BY順序
場(chǎng)景3:Using index(覆蓋索引)
-- 覆蓋索引:查詢字段都在索引中 EXPLAIN SELECT order_id, user_id FROM orders WHERE order_id = 100; -- Extra: Using index(主鍵索引覆蓋) -- 創(chuàng)建覆蓋索引示例 CREATE INDEX idx_cover ON orders(status, created_at, order_id); EXPLAIN SELECT status, created_at, order_id FROM orders WHERE status='pending'; -- Extra: Using index(無需回表)
四、實(shí)戰(zhàn)分析流程
4.1 標(biāo)準(zhǔn)分析步驟
1. 執(zhí)行EXPLAIN,獲取執(zhí)行計(jì)劃 ↓ 2. 檢查type:是否為ALL或index? ↓ 是 → 添加索引 ↓ 否 3. 檢查key:是否為NULL? ↓ 是 → 分析索引失效原因 ↓ 否 4. 檢查rows:是否過大? ↓ 是 → 優(yōu)化索引選擇性 ↓ 否 5. 檢查Extra:是否有Using filesort/Using temporary? ↓ 是 → 優(yōu)化排序/分組索引 ↓ 否 6. 性能良好
4.2 綜合案例分析
案例:訂單報(bào)表查詢慢
EXPLAIN SELECT
DATE(o.created_at) as order_date,
u.user_level,
COUNT(*) as order_count,
SUM(o.order_amount) as total_amount
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
WHERE o.created_at >= '2024-01-01'
AND o.created_at < '2024-02-01'
AND o.order_status IN ('paid', 'shipped')
GROUP BY DATE(o.created_at), u.user_level
ORDER BY order_date DESC, u.user_level;EXPLAIN結(jié)果:
| table | type | key | rows | Extra |
|---|---|---|---|---|
| o | ALL | NULL | 5,234,567 | Using where; Using temporary; Using filesort |
| u | eq_ref | PRIMARY | 1 | NULL |
問題診斷:
- type=ALL:orders全表掃描
- key=NULL:未使用索引
- rows=523萬:掃描全部數(shù)據(jù)
- Using temporary:GROUP BY產(chǎn)生臨時(shí)表
- Using filesort:ORDER BY需要額外排序
優(yōu)化方案:
-- 1. 創(chuàng)建聯(lián)合索引
CREATE INDEX idx_status_created ON orders(order_status, created_at);
-- 2. 改寫SQL,避免DATE()函數(shù)
SELECT
DATE(o.created_at) as order_date,
u.user_level,
COUNT(*) as order_count,
SUM(o.order_amount) as total_amount
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
WHERE o.created_at >= '2024-01-01'
AND o.created_at < '2024-02-01'
AND o.order_status IN ('paid', 'shipped')
GROUP BY DATE(o.created_at), u.user_level
ORDER BY order_date DESC, u.user_level;
-- 3. 考慮使用匯總表(物化視圖)預(yù)處理五、EXPLAIN ANALYZE(MySQL 8.0.18+)
5.1 功能說明
實(shí)際執(zhí)行SQL并返回詳細(xì)的執(zhí)行統(tǒng)計(jì)信息,包括實(shí)際耗時(shí)、實(shí)際行數(shù)等,比EXPLAIN更精確。
EXPLAIN ANALYZE SELECT * FROM orders WHERE order_status = 'pending'\G
輸出示例:
-> Filter: (orders.order_status = 'pending') (cost=101.23 rows=1850) (actual time=0.123..0.456 rows=1234 loops=1)
-> Index lookup on orders using idx_status (order_status='pending') (cost=101.23 rows=1850) (actual time=0.098..0.234 rows=1234 loops=1)關(guān)鍵信息:
actual time:實(shí)際執(zhí)行時(shí)間rows:實(shí)際返回行數(shù)loops:循環(huán)執(zhí)行次數(shù)(被驅(qū)動(dòng)表)
六、優(yōu)化檢查清單
| 檢查項(xiàng) | 理想狀態(tài) | 問題信號(hào) |
|---|---|---|
| type | const/eq_ref/ref/range | ALL/index |
| key | 有索引名 | NULL |
| rows | < 1000或占總行數(shù)<5% | 接近表總行數(shù) |
| Extra | Using index | Using filesort/Using temporary |
| filtered | 高(>30%) | 低(<5%)但rows大 |
七、學(xué)習(xí)建議
- 熟記type優(yōu)先級(jí):const > eq_ref > ref > range > index > ALL
- 重點(diǎn)關(guān)注Extra:filesort和temporary是常見性能殺手
- 結(jié)合業(yè)務(wù)驗(yàn)證rows:預(yù)估掃描行數(shù)是否合理
- 善用SHOW INDEX:了解表索引結(jié)構(gòu)后再分析
- MySQL 8.0用EXPLAIN ANALYZE:獲取實(shí)際執(zhí)行統(tǒng)計(jì)
- 建立知識(shí)庫:記錄常見問題模式(函數(shù)導(dǎo)致索引失效、隱式類型轉(zhuǎn)換等)
到此這篇關(guān)于MySQL EXPLAIN 關(guān)鍵參數(shù)詳細(xì)解釋的文章就介紹到這了,更多相關(guān)MySQL EXPLAIN 關(guān)鍵參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
教你如何恢復(fù)使用MEB備份的MySQL數(shù)據(jù)庫
這篇文章主要介紹了教你如何恢復(fù)使用MEB備份的MySQL數(shù)據(jù)庫的具體方法,需要的朋友可以參考下2016-09-09
mysql報(bào)錯(cuò)sql_mode=only_full_group_by解決
這篇文章主要為大家介紹了mysql報(bào)錯(cuò)sql_mode=only_full_group_by解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
MySql數(shù)據(jù)庫CRUD(增刪改查)操作全流程
CRUD是數(shù)據(jù)庫操作的核心,對(duì)應(yīng)Create(新增)、Read(查詢)、Update(修改)、Delete(刪除)四種基礎(chǔ)操作,這篇文章主要介紹了MySql數(shù)據(jù)庫CRUD(增刪改查)操作的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2026-01-01
Mysql提取JSON對(duì)象和數(shù)組的方法示例代碼
在MySQL中,JSON數(shù)據(jù)類型是一種特殊的數(shù)據(jù)類型,它可以存儲(chǔ)JSON格式的數(shù)據(jù),下面這篇文章主要介紹了Mysql提取JSON對(duì)象和數(shù)組的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-09-09
MySQL數(shù)據(jù)庫表的內(nèi)連接和外連接示例詳解
這篇文章主要介紹了MySQL數(shù)據(jù)庫表的內(nèi)連接和外連接的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),包括左外連接和右外連接的概念、應(yīng)用場(chǎng)景及語法寫法,需要的朋友可以參考下2026-05-05

