MySQL 的 EXPLAIN 命令可以查看SELECT語句的執行的計划,是 MySQL 查詢優化的必備工具。
通過執行計划可以了解查詢方式、索引使用情況、需要掃描的數據量以及是否需要臨時表或排序操作等信息。
我們需要分析執行計划對查詢進行有的放矢的優化。
需要注意:
- EXPLAIN不考慮觸發器、存儲過程或用戶自定義函數對查詢的影響
- EXPLAIN不考慮緩存
- EXPLAIN只能分析執行計划,不能顯示存儲引擎在執行查詢過程中進行的操作
- 部分統計信息是估算的,並非精確值
本文基於 MySQL 5.6 版本。
EXPLAIN SELECT * FROM `user`
JOIN `post` ON `user`.id = `post`.uid
WHERE user.`created_at` < '2018-10-01 00:00:00' AND `post`.status = 1;
結果:
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | user | range | PRIMARY,idx_created_at | idx_created_at | 7 | null | 19440 | Using index condition; Using where; Using temporary; Using filesort |
1 | SIMPLE | post | ref | idx_uid,idx_status | idx_uid | 8 | user.id | 1 | Using where |
EXPLAIN 的行數為查詢涉及的表數, 結果各列的含義為:
- id: 查詢的唯一標識
- select_type: 查詢的類型
- table: 查詢的表, 可能是數據庫中的表/視圖,也可能是 FROM 中的子查詢
- type: 搜索數據的方法
- possible_keys: 可能使用的索引
- key: 最終決定要使用的key
- key_len: 查詢索引使用的字節數。通常越少越好
- ref: 查詢的列或常量
- rows: 需要掃描的行數,估計值。通常越少越好
- extra: 額外的信息
select type
select_type 可能的值有:
- SIMPLE: 簡單查詢,不包含子查詢和union
- PRIMRARY: 包含子查詢時的最外層查詢; 使用union時的第一個查詢
- UNION: 包含union的查詢中非第一個查詢
- DEPENDENT UNION: 與 UNION 相同,但依賴外層查詢的結果
- SUBQUERY: 子查詢
- DEPENDENT SUBQUERY: 依賴外層查詢的子查詢
- DERIVED: 用於 FROM 中的子查詢
下面給出幾個示例:
EXPLAIN SELECT * FROM post WHERE uid = (
SELECT id FROM user WHERE name = "finley"
);
id | select_type | table |
---|---|---|
1 | PRIMARY | post |
2 | SUBQUERY | user |
DEPENDENT SUBQUERY:
EXPLAIN SELECT * FROM post WHERE uid = (
SELECT id FROM user WHERE name = "finley" AND post.uid=user.id
);
id | select_type | table |
---|---|---|
1 | PRIMARY | post |
2 | DEPENDENT SUBQUERY | user |
type
type 字段描述了查詢的方式,從好到壞為:
-
null: 不需要訪問索引和表即可完成, 示例:
SELECT 1;
-
const: 表中僅有一行匹配,在分解查詢計划時直接將其讀出作為常量使用。system 是 const 類型的特例。
示例:SELECT id FROM user WHERE name = "hdt3213";
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE user const uni_name uni_name 258 const 1 Using index
UNIQUE KEY uni_name (name) ON user
-
eq_ref: 使用 PRIMARY KEY 或 UNIQUE KEY 進行關聯查詢。
示例:SELECT * FROM post JOIN user ON post.uid = user.id WHERE user.gender = 'M';
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE post ALL idx_uid 0 0 0 57796 null 1 SIMPLE user eq_ref PRIMARY PRIMARY 8 post.uid 1 Using where -
ref: 使用允許重復的索引進行查詢
示例:SELECT * FROM user WHERE phone='12345678901';
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE user ref idx_phone idx_phone 259 const 1 Using index condition -
range: 使用索引進行范圍查詢:
示例:SELECT * FROM user WHERE age>18;
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE user ref idx_age idx_age 259 const 1 null -
index: 在索引上進行順序掃描。常見於在多列索引中未使用最左列進行查詢。
示例:SELECT * FROM user WHERE last_name='smith'
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE user ref idx_full_name idx_full_name 259 const 1 Using where -
all: 掃描全表,最壞的情況
extra
extra 列顯示了查詢過程中需要執行的其它操作,有些情況應盡力避免。
- using filesort: 查詢時執行了排序操作而無法使用索引排序。雖然名稱為'file'但操作可能是在內存中執行的,取決是否有足夠的內存進行排序。
應盡量避免這種filesort出現。 - using temporary: 使用臨時表存儲中間結果,常見於ORDER BY和GROUP BY語句中。臨時表可能在內存中也可能在硬盤中,應盡量避免這種操作出現。
- using index: 索引中包含查詢的所有列不需要查詢數據表(回表)。可以加快查詢速度。
- using where: 使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給客戶端
- using index condition: 索引條件推送(MySQL 5.6 新特性),服務器層將不能直接使用索引的查詢條件推送給存儲引擎,從而避免在服務器層進行過濾。
- distinct: 優化distinct操作,查詢到匹配的數據后停止繼續搜索