From 31d182f231f951054de29231d9fc97aaf849fe5d Mon Sep 17 00:00:00 2001
From: heibaiying <2806718453@qq.com>
Date: Thu, 25 Jul 2019 14:25:46 +0800
Subject: [PATCH] mysql-explain
---
notes/MySQL_EXPLAIN.md | 130 +++++++++++++++++++++++++----------------
1 file changed, 81 insertions(+), 49 deletions(-)
diff --git a/notes/MySQL_EXPLAIN.md b/notes/MySQL_EXPLAIN.md
index 2627cd7..b5cd27a 100644
--- a/notes/MySQL_EXPLAIN.md
+++ b/notes/MySQL_EXPLAIN.md
@@ -1,8 +1,8 @@
-# MySQL EXPLAIN
+# MySQL EXPLAIN
-EXPLAIN 关键字可以用于获取所修饰语句执行计划的相关信息,在 MySQL 8.0 中,EXPLAIN 支持大多数常见的语句,如 SELECT 、DELETE 、INSERT 、REPLACE、和 UPDATE 语句。示例如下:
+EXPLAIN 关键字可以用于获取 SQL 语句执行计划的相关信息,在 MySQL 8.0 中,EXPLAIN 支持大多数 SQL 语句,如 SELECT 、DELETE 、INSERT 、REPLACE、和 UPDATE 。示例如下:
-```sql
+```shell
mysql> EXPLAIN SELECT * FROM employees;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
@@ -11,21 +11,20 @@ mysql> EXPLAIN SELECT * FROM employees;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
```
-注:本篇文章所有的测试数据来均源于 MySQL 官方提供的 [Employees Sample Database](https://dev.mysql.com/doc/employee/en/),其数据库结构如下:
+注:本篇文章的测试数据来源于 MySQL 官方提供的 [Employees Sample Database](https://dev.mysql.com/doc/employee/en/),其数据库结构如下:
-
-以下对 EXPLAIN 输出结果中各个字段的含义及其可能的取值范围做详细的介绍:
+以下分别介绍 EXPLAIN 输出结果中各个字段的含义:
## 1. id
- id 字段为行标识符,同时也表示该行语句执行的优先级,值越大则优先级越高。如果某行语句引用了其他行结果集的并集,则该值可以为 NULL。示例如下:
+ id 为行标识符,同时也表示语句执行的优先级,值越大则优先级越高。特殊情况下,如果某行语句引用了其他多行结果集的并集,则该值可以为 NULL。示例如下:
-```sql
--- 该FROM字句只是用于演示,并没有任何实际意义
+```shell
+# 该FROM字句只是用于演示,并没有任何实际意义
mysql> EXPLAIN SELECT COUNT(1) FROM (SELECT emp_no FROM salaries) AS t;
+----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
@@ -35,8 +34,8 @@ mysql> EXPLAIN SELECT COUNT(1) FROM (SELECT emp_no FROM salaries) AS t;
+----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
```
-```sql
--- 查询工资大于500000或部门编号等于d007的所有雇员的编号
+```shell
+# 查询工资大于500000或部门编号等于d007的所有雇员的编号
mysql> EXPLAIN SELECT emp_no FROM salaries WHERE salary>500000 UNION ALL SELECT emp_no FROM dept_emp WHERE dept_no = "d007";
+----+--------------+------------+------+---------------+---------+---------+-------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
@@ -49,6 +48,8 @@ mysql> EXPLAIN SELECT emp_no FROM salaries WHERE salary>500000 UNION ALL SELECT
## 2. select_type
+select_type 用于表示查询的类型,常见类型及其含义如下:
+
+ **SIMPLE**:不包含子查询或者 UNION 操作的查询;
+ **PRIMARY**:查询中如果包含任何子查询,那么最外层的查询则被标记为 PRIMARY ;
+ **SUBQUERY**:子查询中第一个 SELECT ;
@@ -58,11 +59,11 @@ mysql> EXPLAIN SELECT emp_no FROM salaries WHERE salary>500000 UNION ALL SELECT
+ **UNION RESULT**:UNION 产生的结果集;
+ **DERIVED**:出现在 FROM 字句中的子查询。
-这里常用的类型在上面的示例中都出现过,下面演示查询类型为 SUBQUERY 的情况,示例如下:
+这里以查询类型为 SUBQUERY 的情况进行演示,示例如下:
-```sql
--- 根据员工编号查询员工姓名及其工资总和
-mysql> explain select first_name,(select sum(salary) from salaries where emp_no = 10001) from employees where emp_no = 10001;
+```shell
+# 根据员工编号查询员工姓名及其工资总和
+mysql> EXPLAIN SELECT first_name,(SELECT sum(salary) FROM salaries WHERE emp_no = 10001) FROM employees WHERE emp_no = 10001;
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
@@ -74,19 +75,19 @@ mysql> explain select first_name,(select sum(salary) from salaries where emp_no
## 3. table
- ****:输出结果中编号为 M 的行与编号为 N 的行的结果集的并集。
-- ****:输出结果中编号为 N 的行的结果集,使用 derived 修饰表示这是一个派生结果集,如来自于 FROM 子句中的查询 。
-- ****:输出结果中编号为 N 的行的结果集,使用 subquery 修饰表示其来源于一个物化子查询。
+- ****:输出结果中编号为 N 的行的结果集,使用 derived 修饰表示这是一个派生结果集,如 FROM 子句中的查询。
+- ****:输出结果中编号为 N 的行的结果集,使用 subquery 修饰表示这是一个物化子查询。
## 4. type
-这个字段非常重要,它表示 MySQL 使用何种方式来查找目标数据,不同查找方式会导致不同的性能开销,常见查找方式及其性能表现按照由高到低的顺序排序如下:
+type 字段是进行性能判断的重要依据,它表示 MySQL 使用何种方式来查找目标数据集,不同查找方式会导致不同的性能开销,常见查找方式及其性能表现按照由高到低的顺序排序如下:
**1. system**:这是 const 类型的一个特例,只会出现在待查询的表只有一行数据的情况下。
-**2. const**:出现在主键或唯一索引与常量值进行比较的场景下,这种情况下查询性能是最优的。
+**2. const**:常出现在主键或唯一索引与常量值进行比较的场景下,此时查询性能是最优的。
-```sql
-mysql> explain select * from employees where emp_no = 10008;
+```shell
+mysql> EXPLAIN SELECT * FROM employees WHERE emp_no = 10008;
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
@@ -94,11 +95,11 @@ mysql> explain select * from employees where emp_no = 10008;
+----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
```
-**3. eq_ref**:在连接查询中,当连接使用索引的所有部分并且索引是 PRIMARY KEY 或 UNIQUE NOT NULL 索引时使用它。
+**3. eq_ref**:通常出现在连接查询中,当连接使用完整的索引并且是 PRIMARY KEY 或 UNIQUE NOT NULL 索引时使用它。
-```sql
--- 这里员工部门关系表 dept_no 的联合主键为 emp_no + dept_no ,即员工编号+部门标号
-mysql> explain select * from employees e,dept_emp d where e.emp_no = d.emp_no and dept_no = "d005";
+```shell
+# 这里员工部门关系表 dept_no 的联合主键为 emp_no + dept_no ,即员工编号+部门标号
+mysql> EXPLAIN SELECT * FROM employees e,dept_emp d WHERE e.emp_no = d.emp_no AND dept_no = "d005";
+----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
@@ -107,11 +108,11 @@ mysql> explain select * from employees e,dept_emp d where e.emp_no = d.emp_no an
+----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
```
-**4. ref**:通常在连接查询中,如果连接仅使用了前缀索引或用于连接的条件不是 PRIMARY KEY 或 UNIQUE 索引时使用它。
+**4. ref**:通常出现在连接查询中,如果连接仅使用了前缀索引或用于连接的条件不是 PRIMARY KEY 或 UNIQUE 索引时使用它。
-```sql
--- 这里仅使用了前缀索引emp_no,所以其类型为 ref , 而不是 eq_ref
-mysql> explain select * from employees e,dept_emp d where e.emp_no = d.emp_no;
+```shell
+# 这里仅使用了前缀索引emp_no,所以其类型为 ref , 而不是 eq_ref
+mysql> EXPLAIN SELECT * FROM employees e,dept_emp d WHERE e.emp_no = d.emp_no;
+----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
@@ -120,7 +121,7 @@ mysql> explain select * from employees e,dept_emp d where e.emp_no = d.emp_no;
+----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
```
-**5. ref_or_null**:类似于 ref 类型的查询,但是附加了对 NULL 值列的查询。这里给出示例语句:
+**5. ref_or_null**:类似于 ref 类型的查询,但是附加了对 NULL 值列的查询。示例语句如下:
```sql
SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
@@ -128,8 +129,8 @@ SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
**6. index_merge**:该联接类型表示使用了索引进行合并优化,示例如下:
-```
-mysql> explain select * from dept_emp where dept_no = "d004" and emp_no < 10020;
+```shell
+mysql> EXPLAIN SELECT * FROM dept_emp WHERE dept_no = "d004" AND emp_no < 10020;
+----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
@@ -139,8 +140,8 @@ mysql> explain select * from dept_emp where dept_no = "d004" and emp_no < 10020;
**7. range**:使用索引进行范围扫描,常见于 between、> 、< 这样的查询条件。
-```sql
-mysql> explain select * from employees where emp_no > 10000;
+```shell
+mysql> EXPLAIN SELECT * FROM employees WHERE emp_no > 10000;
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
@@ -150,8 +151,8 @@ mysql> explain select * from employees where emp_no > 10000;
**8. index**:索引连接类型与 ALL 相同,只是扫描的是索引树,通常出现在索引是该查询的覆盖索引的情况:
-```sql
-mysql> explain select emp_no from employees;
+```shell
+mysql> EXPLAIN SELECT emp_no FROM employees;
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
@@ -161,8 +162,8 @@ mysql> explain select emp_no from employees;
**9. ALL**:全表扫描,效率最差的查找方式。
-```sql
-mysql> explain select * from employees where first_name ="Bezalel";
+```shell
+mysql> EXPLAIN SELECT * FROM employees WHERE first_name ="Bezalel";
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
@@ -192,24 +193,55 @@ MySQL 为了找到目标行而读取的所有行的数量,这是一个估算
## 10. Extra
-用于显示额外的信息,常见参数如下:
+Extra 列主要用于显示额外的信息,常见信息及其含义如下:
-+ **Distinct**:优化 distinct 操作,再找到第一匹配的元组后即停止找同样值的动作;
-+ **Not exists**:使用 not exists 来优化查询;
-+ **Using filesort**:使用额外操作进行排序,通常会出现在 order by 或 group by 查询中;
-+ **Using index**:使用了覆盖索引进行查询;
-+ **Using temporary**:使用临时表来处理查询,常见于排序、子查询和分组查询;
-+ **Using where**:使用 WHERE 条件来过滤数据;
-+ **select tables optimized away**:直接通过索引来获取数据,不需要访问表。
+**1. Using where** :MySQL 服务器会在存储引擎检索行后再进行过滤。示例如下:
-## 11. partitions
+```shell
+# first_name 字段是一个普通的列,非索引列
+mysql> EXPLAIN SELECT * FROM employees WHERE first_name = "Sumant";
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
+| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 299379 | Using where |
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
+```
-显示查询结果集中记录所在的分区。如果目标表不是分区表,则值为 null 。
+**2. Using filesort**:通常出现在 GROUP BY 或 ORDER BY 语句中,且排序或分组没有基于索引,此时需要使用文件在内存中进行排序。因为使用索引排序的性能好于使用文件排序,所以出现这种情况可以考虑通过添加索引进行优化。示例如下:
+```shell
+mysql> EXPLAIN SELECT * FROM employees ORDER BY first_name ;
++----+-------------+-----------+------+---------------+------+---------+------+--------+----------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+------+---------------+------+---------+------+--------+----------------+
+| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 299379 | Using filesort |
++----+-------------+-----------+------+---------------+------+---------+------+--------+----------------+
+```
+**3. Using index**:使用了覆盖索引进行查询,此时不需要访问表,从索引中就可以获取到所需的全部数据。示例如下:
+
+```shell
+mysql> EXPLAIN SELECT emp_no FROM employees;
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+| 1 | SIMPLE | employees | index | NULL | PRIMARY | 4 | NULL | 299379 | Using index |
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+```
+
+**4. Using temporary**:表示需要使用临时表来处理查询,常出现在 GROUP BY 或 ORDER BY 语句中,示例如下:
+
+```shell
+mysql> EXPLAIN SELECT first_name,count(first_name) FROM employees GROUP BY first_name ;
++----+-------------+-----------+------+---------------+------+---------+------+--------+---------------------------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+------+---------------+------+---------+------+--------+---------------------------------+
+| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 299379 | Using temporary; Using filesort |
++----+-------------+-----------+------+---------------+------+---------+------+--------+---------------------------------+
+```
## 参考资料
-MySQL官方文档:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
+更多参数的说明可以参考 MySQL官方文档:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html