MySQL EXPLAIN詳細解析
EXPLAIN 是 SQL 性能優(yōu)化的關(guān)鍵工具,它展示了 MySQL 如何執(zhí)行一條 SQL 語句。通過分析它的結(jié)果,你可以找出查詢的瓶頸并進行優(yōu)化。
下面我將對 EXPLAIN 進行詳細解析。
一、什么是 EXPLAIN?
EXPLAIN 關(guān)鍵字可以放在 SELECT、DELETE、INSERT、REPLACE 或 UPDATE 語句之前,MySQL 會返回該語句的執(zhí)行計劃,而不是真正執(zhí)行它。
執(zhí)行計劃揭示了 MySQL 優(yōu)化器決定如何訪問表、使用哪些索引、表之間的連接方式等信息。
基本語法:
EXPLAIN your_sql_statement; -- 例如 EXPLAIN SELECT * FROM users WHERE name = 'John';
在 MySQL 8.0 之后,推薦使用 EXPLAIN FORMAT=TRADITIONAL(默認(rèn)格式),或者更詳細的 EXPLAIN ANALYZE(MySQL 8.0.18+ 引入,會實際執(zhí)行查詢并給出更精確的分析)。
二、EXPLAIN 輸出列詳解
執(zhí)行 EXPLAIN 后,你會得到一個包含多列的結(jié)果集。以下是這些列的含義,其中前幾列(type, key, rows, Extra)最為重要。
| 列名 | 描述 | 重要性 |
|---|---|---|
| id | 查詢中每個 SELECT 子句的唯一標(biāo)識符。如果相同,則按順序執(zhí)行;如果不同,id 越大優(yōu)先級越高(先執(zhí)行)。 | 中 |
| select_type | 查詢的類型,如 SIMPLE, PRIMARY, SUBQUERY, DERIVED 等。 | 中 |
| table | 正在訪問的表名。 | 低 |
| partitions | 匹配的分區(qū),非分區(qū)表則為 NULL。 | 低 |
| type | 連接類型 或 訪問類型。這是衡量查詢性能的最關(guān)鍵指標(biāo)之一。 | 高 |
| possible_keys | 查詢中可能用到的索引。 | 中 |
| key | 查詢中實際決定使用的索引。為 NULL 則表示未使用索引。 | 高 |
| key_len | 使用的索引鍵的長度。可用于判斷是否充分利用了復(fù)合索引。 | 中 |
| ref | 顯示索引的哪一列被使用了,通常是一個常量(const)或另一個表的列名。 | 中 |
| rows | MySQL 估計為了找到所需的行而需要讀取的行數(shù)。這個值越小越好。 | 高 |
| filtered | 表示存儲引擎返回的數(shù)據(jù)在服務(wù)器層過濾后,剩余多少比例滿足查詢條件。理想是 100。 | 中 |
| Extra | 包含不適合在其他列顯示的額外信息,非常多的重要信息在這里。 | 高 |
三、核心列深度解析
1.type(訪問類型)
性能從最優(yōu)到最差排序如下:
system > const > eq_ref > ref > range > index > ALL
system:表只有一行記錄(等于系統(tǒng)表),是 const 類型的特例。
const:通過索引一次就找到了,用于比較 主鍵索引 或 唯一索引 與常數(shù)值。速度極快。
EXPLAIN SELECT * FROM users WHERE id = 1; -- `id` 是主鍵
eq_ref:在連接查詢時,使用 主鍵 或 唯一非空索引 進行關(guān)聯(lián)。對于來自前表的每一行,從本表中只返回一行。這是除了 system 和 const 之外最好的連接類型。
EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id; -- `o.user_id` 是 `users` 表的主鍵 `id` 的外鍵,并且是唯一索引
ref:使用 非唯一性索引 進行掃描,返回匹配某個單獨值的所有行。
EXPLAIN SELECT * FROM users WHERE name = 'John'; -- `name` 字段上有一個普通索引(非唯一)
range:只檢索給定范圍的行,使用一個索引來選擇行。關(guān)鍵運算符是 BETWEEN、>、<、IN 等。
EXPLAIN SELECT * FROM users WHERE id > 10; EXPLAIN SELECT * FROM users WHERE id IN (1, 2, 3);
index:全索引掃描。遍歷整個索引樹來查找數(shù)據(jù),比 ALL 快一點,因為索引文件通常比數(shù)據(jù)文件小。
EXPLAIN SELECT id FROM users; -- 查詢的列 `id` 正好是索引的一部分,直接從索引中讀取,無需回表
ALL:全表掃描。性能最差,意味著 MySQL 會遍歷整張表來找到匹配的行。必須優(yōu)化。
2.rows(預(yù)估行數(shù))
這不是查詢結(jié)果的行數(shù),而是 MySQL 為了找到目標(biāo)記錄,預(yù)估需要掃描多少行。這是一個基于統(tǒng)計信息的預(yù)估值。這個值越小越好,說明查詢效率高。
3.Extra(額外信息)
這里包含大量細節(jié),是判斷查詢質(zhì)量的另一個關(guān)鍵。
- Using index:覆蓋索引。查詢的列都包含在索引中,無需回表查詢數(shù)據(jù)行。性能極佳。
-- 假設(shè)在 `name` 和 `age` 上有一個復(fù)合索引 (name, age) EXPLAIN SELECT name, age FROM users WHERE name = 'John';
- Using where:表示在存儲引擎返回行后,MySQL 服務(wù)器層再次進行了過濾。如果
type是ALL或index,并且出現(xiàn)Using where,通常意味著性能不佳。 - Using temporary:MySQL 需要創(chuàng)建一張臨時表來處理查詢。常見于
GROUP BY和ORDER BY的子句涉及不同列時。需要優(yōu)化。 - Using filesort:MySQL 無法使用索引對結(jié)果進行排序,需要額外的排序步驟。如果數(shù)據(jù)量大,會非常消耗資源。需要優(yōu)化。
- Using join buffer:表示連接查詢時,沒有使用索引,需要用到連接緩沖區(qū)。通常意味著連接字段上沒有索引。
四、實戰(zhàn)分析示例
假設(shè)我們有兩張表:
users表id(主鍵)name(有普通索引)
emailorders表id(主鍵)user_id(外鍵,關(guān)聯(lián)users.id,有索引)
amount
查詢1:簡單的等值查詢
EXPLAIN SELECT * FROM users WHERE name = 'Alice';
可能的結(jié)果分析:
type: ref (使用了非唯一索引)key: name (實際使用了name索引)rows: 1 (預(yù)估掃描1行)Extra: (空,或者Using index如果查詢的列都被索引覆蓋)
結(jié)論: 這是一個高效的查詢。
查詢2:連接查詢
EXPLAIN SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE u.name = 'Bob';
可能的結(jié)果分析(對于 orders 表這一行):
type: ref (使用user_id索引來關(guān)聯(lián))key: user_idrows: 5 (預(yù)估每個用戶平均有5個訂單)Extra: (空)
結(jié)論: 連接效率很高,因為雙方都使用了索引。
查詢3:性能不佳的查詢
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com' ORDER BY name;
可能的結(jié)果分析:
type: ALL (全表掃描,因為email字段沒有索引)key: NULLrows: 10000 (表中有1萬行數(shù)據(jù))Extra: Using filesort (在內(nèi)存或磁盤上進行排序)
結(jié)論: 這是一個災(zāi)難性的查詢。它進行了全表掃描,并且還有一個昂貴的文件排序。
優(yōu)化建議:
- 為
email字段添加索引。 - 如果經(jīng)常需要按
name排序,可以考慮建立(email, name)的復(fù)合索引,這樣可以利用索引來查找和排序。
五、進階工具:EXPLAIN ANALYZE (MySQL 8.0.18+)
EXPLAIN ANALYZE 會實際執(zhí)行查詢,并提供一個更詳細的、包含實際執(zhí)行時間的分析報告。
EXPLAIN ANALYZE SELECT * FROM users WHERE name = 'John';
輸出格式類似于:
-> Index lookup on users using idx_name (name='John') (cost=0.35 rows=1) (actual time=0.025..0.027 rows=1 loops=1)
它提供了:
- 實際執(zhí)行時間 (
actual time)。 - 實際返回行數(shù) (
rows)。 - 執(zhí)行循環(huán)次數(shù) (
loops)。 - 預(yù)估成本 (
cost)。
這比傳統(tǒng)的 EXPLAIN 提供了更精確的性能視圖。
總結(jié)
| 檢查點 | 目標(biāo) |
|---|---|
| type 列 | 至少達到 range 級別,最好能到 ref。避免 ALL。 |
| key 列 | 確保查詢實際使用了合適的索引,不為 NULL。 |
| rows 列 | 預(yù)估掃描行數(shù)盡可能小。 |
| Extra 列 | 追求出現(xiàn) Using index。警惕 Using temporary 和 Using filesort。 |
熟練掌握 EXPLAIN 是每個后端開發(fā)者和 DBA 的必備技能,它能幫助你從“猜測”優(yōu)化變?yōu)?ldquo;數(shù)據(jù)驅(qū)動”的優(yōu)化。
到此這篇關(guān)于MySQL EXPLAIN 詳解與優(yōu)化指南的文章就介紹到這了,更多相關(guān)mysql explain內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Navicat配置mysql數(shù)據(jù)庫用戶權(quán)限問題
這篇文章主要介紹了Navicat配置mysql數(shù)據(jù)庫用戶權(quán)限問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
MySQL在讀已提交和可重復(fù)讀這兩個不同事務(wù)隔離級別下幻讀的區(qū)別及說明
這篇文章主要介紹了MySQL在讀已提交和可重復(fù)讀這兩個不同事務(wù)隔離級別下幻讀的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-06-06
Window環(huán)境下MySQL?UDF提權(quán)
本文章僅記錄某次內(nèi)網(wǎng)滲透過程中遇到的MySQL?采用UDF提權(quán)等方式進行獲取權(quán)限,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>2023-03-03
MySQL安裝與配置:如何重置MySQL登錄密碼(windows環(huán)境)
這篇文章主要介紹了MySQL安裝與配置:如何重置MySQL登錄密碼(windows環(huán)境),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
Windows下MySQL服務(wù)啟動常見的兩種方式(適配5.7和8.0)
本文主要介紹了Windows下MySQL服務(wù)啟動常見的兩種方式(適配5.7和8.0),文中通過圖文介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07
MySQL中安裝樣本數(shù)據(jù)庫Sakila過程分享
這篇文章主要介紹了MySQL中安裝樣本數(shù)據(jù)庫Sakila過程分享,Sakila數(shù)據(jù)庫主要用來做一些基本的操作以及壓力測試等,需要的朋友可以參考下2014-10-10

