mysql-EXPLAIN语法详解

MYSQL 中的EXPLAIN 语句可以用来分析查询语句,分析语句的执行情况。
基本语法是:

EXPLAIN SELECT select_options

结果简述:

字段 取值和含义
id select 标识符,查询序号表示执行的优先级
select_type 表示SELECT语句的类型【SIMPLE,PRIMARY,UNION,UNION RESULT,…】
table 查询的哪张表
partitions
type 表示表的连接类型【system,const,eq_ref…】
possible_keys 可以使用哪个索引来进行查询,NULL则表示没有用到索引
key 实际查询用到的索引,NULL则表示没有用到索引
key_len 表示索引字段的字节长度
ref 表示使用哪个列或常数与索引一起来查询记录
rows 查询是需要扫描的行数
filtered
Extra 处理查询时的详细信息【using index ,using where,using temporary,using filesort】

示例,已下面这张表为例

mysql> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no     | int(11)       | NO   | PRI | NULL    |       |
| birth_date | date          | NO   |     | NULL    |       |
| first_name | varchar(14)   | NO   |     | NULL    |       |
| last_name  | varchar(16)   | NO   |     | NULL    |       |
| gender     | enum('M','F') | NO   |     | NULL    |       |
| hire_date  | date          | NO   |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.00 sec)

mysql> select count(1) from employees;
+----------+
| count(1) |
+----------+
|   300024 |
+----------+
1 row in set (0.18 sec)

mysql> show index from employees;
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table     | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees |          0 | PRIMARY  |            1 | emp_no      | A         |      299423 |     NULL | NULL   |      | BTREE      |         |               |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

  • select * from employees where emp_no=10001
mysql> explain select * from employees  where emp_no=10001;
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.38 sec)

  • select * from employees where first_name=‘Georgi’ and last_name=‘Facello’
mysql> explain select * from employees where first_name='Georgi' and last_name='Facello';
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299423 |     1.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

从以上两个EXPLAIN来看结果分析:

语句1:select * from employees where emp_no=10001
一个简单查询,使用了主键索引,扫描行数为1

语句2:select * from employees where first_name=‘Georgi’ and last_name=‘Facello’
一个简单查询,未使用索引,做了全表扫描

针对查询2的优化 可以添加一个联合索引
分析联合索引的离散度

mysql> select count(distinct(concat(first_name,last_name))) from employees;
+-----------------------------------------------+
| count(distinct(concat(first_name,last_name))) |
+-----------------------------------------------+
|                                        279408 |
+-----------------------------------------------+
1 row in set (0.58 sec)

这两个字段做联合后离散度很高,适合添加索引

mysql> alter table employees add index index_full_name(first_name,last_name);
Query OK, 0 rows affected (3.97 sec)
Records: 0  Duplicates: 0  Warnings: 0

再分析语句2

mysql>  explain select * from employees where first_name='Georgi' and last_name='Facello';
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys   | key             | key_len | ref         | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | index_full_name | index_full_name | 94      | const,const |    2 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

使用了索引,只扫描2行,效率提高
如果变更条件

使用了索引,扫描253行数据
mysql> explain select * from employees where first_name='Georgi' ;
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | index_full_name | index_full_name | 44      | const |  253 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

未使用索引,不满足最左匹配原则
mysql> explain select * from employees where last_name='Facello' ;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 299423 |    10.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

不满足最左匹配原则,但是使用了覆盖索引,因结果集数据存在于index_full_name ,不需要回表
mysql> explain select first_name,last_name  from employees where last_name='Facello' ;
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+--------+----------+--------------------------+
| id | select_type | table     | partitions | type  | possible_keys | key             | key_len | ref  | rows   | filtered | Extra                    |
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+--------+----------+--------------------------+
|  1 | SIMPLE      | employees | NULL       | index | NULL          | index_full_name | 94      | NULL | 299423 |    10.00 | Using where; Using index |
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+--------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

使用了索引,且使用了覆盖索引
mysql> explain select first_name,last_name  from employees where first_name='Georgi' ;
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | ref  | index_full_name | index_full_name | 44      | const |  253 |   100.00 | Using index |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

看起来不满足最左匹配原则,但是优化器Query Optimizer 对下列语句做了优化,变成 
select * from employees where first_name='Georgi' and last_name='Facello';
mysql> explain select * from employees where last_name='Facello' and  first_name='Georgi' ;
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys   | key             | key_len | ref         | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ref  | index_full_name | index_full_name | 94      | const,const |    2 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

分析底层存储

#添加索引前
-rw-r----- 1 mysql mysql      8768 4月  19 21:47 employees.frm
-rw-r----- 1 mysql mysql  23068672 4月  19 21:48 employees.ibd
#添加索引后
-rw-r----- 1 mysql mysql      8768 4月  21 23:01 employees.frm
-rw-r----- 1 mysql mysql  30408704 4月  21 23:01 employees.ibd

很明显,因为添加了一个索引后,又多生成了一个B+tree,数据文件更大

你可能感兴趣的:(mysql)