EXPLAIN
是 MySQL 中用于分析查詢執行計劃的重要工具,通過它可以查看查詢如何使用索引、掃描數據的方式以及表連接順序等信息,從而找出性能瓶頸。以下是關于EXPLAIN
的詳細介紹和實戰指南:
1. EXPLAIN 基本用法
在SELECT
、INSERT
、UPDATE
、DELETE
語句前加上EXPLAIN
關鍵字即可查看執行計劃:
EXPLAIN SELECT * FROM users WHERE age > 18;
2. 關鍵字段解析
EXPLAIN
返回的結果包含多個字段,重點關注以下幾個:
id
- 查詢的標識符,數值越大優先級越高,相同數值按順序執行。
type
- 數據訪問類型,從最優到最差排序:
system
/const
:單條記錄查詢(主鍵或唯一索引)。eq_ref
:唯一索引掃描(如JOIN
操作)。ref
:非唯一索引掃描。range
:范圍掃描(如WHERE age > 18
)。index
:全索引掃描(僅掃描索引樹)。ALL
:全表掃描(性能最差)。
possible_keys
- 可能使用的索引,但不一定實際使用。
key
- 實際使用的索引,若為
NULL
則未使用索引。
key_len
- 索引使用的字節數,用于評估索引的選擇性。
rows
- MySQL 估算的掃描行數,值越小越好。
Extra
- 額外信息,常見值:
Using filesort
:需額外排序(性能開銷大)。Using temporary
:使用臨時表(如GROUP BY
或ORDER BY
)。Using index
:覆蓋索引(僅通過索引即可獲取所有數據)。
3. 實戰優化案例
案例 1:全表掃描優化
問題 SQL:
SELECT * FROM orders WHERE status = 'paid';
EXPLAIN 結果:
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | orders | ALL | NULL | NULL | NULL | NULL | 100000 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
分析:type=ALL
(全表掃描),key=NULL
(未使用索引)。
優化:
ALTER TABLE orders ADD INDEX idx_status (status);
優化后 EXPLAIN:
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| 1 | SIMPLE | orders | ref | idx_status | idx_status | 152 | const | 500 | Using index |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
結果:type=ref
(索引掃描),rows=500
(掃描行數大幅減少),Using index
(覆蓋索引)。
案例 2:復合索引優化
問題 SQL:
SELECT user_id, amount FROM orders WHERE user_id = 100 AND status = 'paid';
EXPLAIN 結果:
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| 1 | SIMPLE | orders | ref | idx_user | idx_user | 4 | const | 1000 | Using where |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
分析:僅使用了user_id
索引,未使用status
條件。
優化:創建復合索引:
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);
優化后 EXPLAIN:
+----+-------------+--------+------+-------------------+-------------------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+-------------------+-------------------+---------+-------------+------+-------------+
| 1 | SIMPLE | orders | ref | idx_user_status | idx_user_status | 156 | const,const | 50 | Using index |
+----+-------------+--------+------+-------------------+-------------------+---------+-------------+------+-------------+
結果:rows=50
(掃描行數進一步減少),Using index
(覆蓋索引)。
案例 3:消除Using filesort
問題 SQL:
SELECT * FROM products ORDER BY create_time LIMIT 10;
EXPLAIN 結果:
+----+-------------+----------+------+---------------+------+---------+------+--------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+--------+----------------+
| 1 | SIMPLE | products | ALL | NULL | NULL | NULL | NULL | 100000 | Using filesort |
+----+-------------+----------+------+---------------+------+---------+------+--------+----------------+
分析:全表掃描后進行文件排序(Using filesort
)。
優化:添加索引:
ALTER TABLE products ADD INDEX idx_create_time (create_time);
優化后 EXPLAIN:
+----+-------------+----------+------+---------------+-------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+-------------------+---------+------+------+-------------+
| 1 | SIMPLE | products | index| NULL | idx_create_time | 5 | NULL | 10 | Using index |
+----+-------------+----------+------+---------------+-------------------+---------+------+------+-------------+
結果:type=index
(索引掃描),消除了Using filesort
。
4. 高級用法:EXPLAIN ANALYZE
MySQL 8.0+ 支持EXPLAIN ANALYZE
,返回更詳細的執行信息,包括實際掃描行數和時間:
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 18;
5. 優化建議
- 優先優化
type
字段:盡量避免ALL
和index
類型,目標是ref
或更優。 - 確保
key
字段非NULL
:通過創建索引讓查詢使用索引。 - 消除
Using filesort
和Using temporary
:通過合理索引避免額外排序和臨時表。 - 利用覆蓋索引:讓
Extra
字段出現Using index
,減少回表操作。 - 復合索引順序:將選擇性高的字段放在前面(如唯一值多的字段)。
6. 常見誤區
- 索引越多越好:過多索引會增加寫操作開銷和內存占用。
- 忽視復合索引順序:不滿足最左匹配原則會導致索引失效。
- 過度依賴
EXPLAIN
估算:rows
是估算值,實際可能有偏差,需結合SHOW PROFILE
等工具驗證。
通過EXPLAIN
深入分析查詢執行計劃,針對性地優化索引和查詢語句,可以顯著提升 MySQL 性能。