@@ -29,13 +29,33 @@ MySQL 为我们提供了 `EXPLAIN` 命令,来获取执行计划的相关信息
2929MySQL 8.0.18 引入了 ` EXPLAIN ANALYZE ` ,它会** 真正执行** 查询并输出每个步骤的实际耗时与行数,比标准 ` EXPLAIN ` 的估算数据更可靠,适合在测试环境深度排查慢查询:
3030
3131``` sql
32- EXPLAIN ANALYZE SELECT * FROM dept_emp WHERE emp_no = 10001 ;
32+ mysql> EXPLAIN ANALYZE SELECT * FROM users WHERE age = 25 \G
33+ *************************** 1 . row ***************************
34+ EXPLAIN: - > Covering index lookup on users using idx_age_score_name (age= 25 )
35+ (cost= 1 .52 rows= 12 ) (actual time = 0 .0272 ..0 .0344 rows= 12 loops= 1 )
3336```
3437
3538此外,` EXPLAIN FORMAT=JSON ` 可以输出优化器的成本模型数据(` query_cost ` ),比表格形式更能反映各步骤的实际代价,在多表 JOIN 或子查询调优时尤为有用:
3639
3740``` sql
38- EXPLAIN FORMAT= JSON SELECT * FROM dept_emp WHERE emp_no = 10001 ;
41+ mysql> EXPLAIN FORMAT= JSON SELECT * FROM users WHERE age = 25 \G
42+ *************************** 1 . row ***************************
43+ EXPLAIN: {
44+ " query_block" : {
45+ " select_id" : 1 ,
46+ " cost_info" : {
47+ " query_cost" : " 1.52"
48+ },
49+ " table" : {
50+ " table_name" : " users" ,
51+ " access_type" : " ref" ,
52+ " key" : " idx_age_score_name" ,
53+ " rows_examined_per_scan" : 12 ,
54+ " filtered" : " 100.00" ,
55+ " using_index" : true
56+ }
57+ }
58+ }
3959```
4060
4161` EXPLAIN ` 执行计划支持 ` SELECT ` 、` DELETE ` 、` INSERT ` 、` REPLACE ` 以及 ` UPDATE ` 语句。我们一般多用于分析 ` SELECT ` 查询语句,使用起来非常简单,语法如下:
@@ -46,14 +66,29 @@ EXPLAIN SELECT 查询语句;
4666
4767我们简单来看下一条查询语句的执行计划:
4868
69+ ** 示例 1:单表查询(使用索引)**
70+
71+ ``` sql
72+ -- 表结构:users(id, age, score, name, address),联合索引 idx_age_score_name(age, score, name)
73+ mysql> EXPLAIN SELECT * FROM users WHERE age = 25 ;
74+ + -- --+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
75+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
76+ + -- --+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
77+ | 1 | SIMPLE | users | NULL | ref | idx_age_score_name | idx_age_score_name | 5 | const | 12 | 100 .00 | Using index |
78+ + -- --+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
79+ ```
80+
81+ ** 示例 2:UNION 查询(id 为 NULL 的场景)**
82+
4983``` sql
50- mysql> explain SELECT * FROM dept_emp WHERE emp_no IN (SELECT emp_no FROM dept_emp GROUP BY emp_no HAVING COUNT (emp_no)> 1 );
51- + -- --+-------------+----------+------------+-------+-----------------+---------+---------+------+--------+----------+-------------+
52- | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
53- + -- --+-------------+----------+------------+-------+-----------------+---------+---------+------+--------+----------+-------------+
54- | 1 | PRIMARY | dept_emp | NULL | ALL | NULL | NULL | NULL | NULL | 331143 | 100 .00 | Using where |
55- | 2 | SUBQUERY | dept_emp | NULL | index | PRIMARY,dept_no | PRIMARY | 16 | NULL | 331143 | 100 .00 | Using index |
56- + -- --+-------------+----------+------------+-------+-----------------+---------+---------+------+--------+----------+-------------+
84+ mysql> EXPLAIN SELECT * FROM users WHERE id = 1 UNION SELECT * FROM users WHERE id = 2 ;
85+ + -- --+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
86+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
87+ + -- --+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
88+ | 1 | PRIMARY | users | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100 .00 | NULL |
89+ | 2 | UNION | users | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100 .00 | NULL |
90+ | 3 | UNION RESULT | < union1,2 > | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
91+ + -- --+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
5792```
5893
5994可以看到,执行计划结果中共有 12 列,各列代表的含义总结如下表:
@@ -90,12 +125,28 @@ mysql> explain SELECT * FROM dept_emp WHERE emp_no IN (SELECT emp_no FROM dept_e
90125** 示例** :
91126
92127``` sql
93- EXPLAIN SELECT * FROM dept_emp WHERE emp_no = 10001
94- UNION
95- SELECT * FROM dept_emp WHERE dept_no = ' d001' ;
128+ mysql> EXPLAIN SELECT * FROM users WHERE id = 1
129+ - > UNION
130+ - > SELECT * FROM users WHERE id = 2 \G
131+ *************************** 1 . row ***************************
132+ id: 1
133+ select_type: PRIMARY
134+ table: users
135+ type: const
136+ *************************** 2 . row ***************************
137+ id: 2
138+ select_type: UNION
139+ table: users
140+ type: const
141+ *************************** 3 . row ***************************
142+ id: NULL
143+ select_type: UNION RESULT
144+ table: < union1,2 >
145+ type: ALL
146+ Extra: Using temporary
96147```
97148
98- 输出中最后一行的 ` id = NULL ` ,table = ` <union1,2> ` ,表示这是前两个查询结果的合并。
149+ 第三行的 ` id = NULL ` ,table = ` <union1,2> ` ,表示这是前两个查询结果的合并。
99150
100151### select_type
101152
@@ -180,10 +231,16 @@ rows 列表示根据表统计信息及索引选用情况,**估算**出找到
180231
181232``` sql
182233-- 执行计划估算行数
183- EXPLAIN SELECT * FROM dept_emp WHERE emp_no = 10001 ;
234+ mysql> EXPLAIN SELECT * FROM users WHERE age = 25 \G
235+ rows: 12
184236
185237-- 实际行数(注意:在大表上慎用 COUNT(*))
186- SELECT COUNT (* ) FROM dept_emp WHERE emp_no = 10001 ;
238+ mysql> SELECT COUNT (* ) FROM users WHERE age = 25 ;
239+ + -- --------+
240+ | COUNT (* ) |
241+ + -- --------+
242+ | 12 |
243+ + -- --------+
187244```
188245
189246遇到执行计划与实际性能不符时,可以执行 ` ANALYZE TABLE ` 重新采样,再观察执行计划的变化。
0 commit comments