MySQL性能分析之explain

是什么

使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。

Mysql常见瓶颈

在这里插入图片描述

EXPLAN能干什么

  1. 表的读取顺序:体现在id上
  2. 数据读取操作的操作类型:体现在select_type上
  3. 那些索引可以使用:体现在possible_keys
  4. 那些索引被实际使用:体现在key上
  5. 表之间的引用:
  6. 每张表有多少行被优化器查询:体现在rows上

EXPLAN怎么用

用法:explain+sql语句

执行计划包含的信息

+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+

ID

作用:体现了表的读取顺序

  1. id相同:按顺序读取
  2. id不同:从大到小进行读取
  3. 有的id相同,有的id不同:先从大到小读取,相同的按顺序读取

select_type

作用:标记了查询的类型

  1. simple:简单查询,查询中不包含任何子查询和union
mysql> explain select * from tbl_emp;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | tbl_emp | ALL  | NULL          | NULL | NULL    | NULL |    8 | NULL  |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
  1. primary:查询中若包含任何复杂子查询,外层查询则被标记为primary
mysql> explain select * from tbl_emp a where a.deptld = (select id from tbl_dept b where b.deptName = 'FD');
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key        | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
|  1 | PRIMARY     | a     | ref  | fk_dept_id    | fk_dept_id | 5       | const |    1 | Using where |
|  2 | SUBQUERY    | b     | ALL  | NULL          | NULL       | NULL    | NULL  |    7 | Using where |
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
  1. subquery:在select或where中包含的子查询被标记为subquery
mysql> explain select * from tbl_emp a where a.deptld = (select id from tbl_dept b where b.deptName = 'FD');
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key        | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
|  1 | PRIMARY     | a     | ref  | fk_dept_id    | fk_dept_id | 5       | const |    1 | Using where |
|  2 | SUBQUERY    | b     | ALL  | NULL          | NULL       | NULL    | NULL  |    7 | Using where |
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
  1. derived:在from列表中包含的子查询被标记为derived,Mysql会递归执行这些子查询,把结果放在临时表里
mysql> explain select locAdd from(select * from tbl_dept a where a.deptName = 'HR') b;
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
|  1 | PRIMARY     | <derived2> | ALL  | NULL          | NULL | NULL    | NULL |    7 | NULL        |
|  2 | DERIVED     | a          | ALL  | NULL          | NULL | NULL    | NULL |    7 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
  1. union: 若第二个select出现在union之后,则被标记为union;若union包含在from子句的子查询中,外部select将被标记
mysql> explain select * from tbl_dept a where a.deptName = 'HR' union select * from tbl_dept b where b.deptName = 'RD';
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra           |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
|  1 | PRIMARY      | a          | ALL  | NULL          | NULL | NULL    | NULL |    7 | Using where     |
|  2 | UNION        | b          | ALL  | NULL          | NULL | NULL    | NULL |    7 | Using where     |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL | NULL    | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
  1. union result:从union表获取结果的select
mysql> explain select * from tbl_dept a where a.deptName = 'HR' union select * from tbl_dept b where b.deptName = 'RD';
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra           |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
|  1 | PRIMARY      | a          | ALL  | NULL          | NULL | NULL    | NULL |    7 | Using where     |
|  2 | UNION        | b          | ALL  | NULL          | NULL | NULL    | NULL |    7 | Using where     |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL | NULL    | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+

table

作用:这行查询记录是属于哪一张表的

type

作用:显示查询使用了何种类型
最好到最差排列:system>const>eg_ref>ref>range>all
一般来说查询至少达到renge级别,最好为ref

  1. system:表只有一行记录(等于系统表),平时不会出现,可以忽略不计
  2. const:表示通过索引一次就查询到了,const用于比较primary key或者unique索引。因为只匹配一行数据所以很快。如将主键置于where列表中,Mysql就能将该查询转化为一个常量。
mysql> explain select * from tbl_dept where id = 1;
+----+-------------+----------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | tbl_dept | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+----------+-------+---------------+---------+---------+-------+------+-------+
  1. eg_ref:唯一性索引扫描,对每一个索引键,只有一条记录与之匹配。常见于主键或者唯一性扫描。
mysql> explain select * from tbl_emp a,tbl_dept b where a.deptld = b.id and b.id = 4;
+----+-------------+-------+-------+---------------+------------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key        | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+------------+---------+-------+------+-------+
|  1 | SIMPLE      | b     | const | PRIMARY       | PRIMARY    | 4       | const |    1 | NULL  |
|  1 | SIMPLE      | a     | ref   | fk_dept_id    | fk_dept_id | 5       | const |    1 | NULL  |
+----+-------------+-------+-------+---------------+------------+---------+-------+------+-------+
  1. ref:非唯一性扫描,返回匹配某个单独值的所有行。
mysql> explain select * from tbl_emp a , tbl_dept b where a.deptld = b.id;
+----+-------------+-------+------+---------------+------------+---------+-----------+------+-------+
| id | select_type | table | type | possible_keys | key        | key_len | ref       | rows | Extra |
+----+-------------+-------+------+---------------+------------+---------+-----------+------+-------+
|  1 | SIMPLE      | b     | ALL  | PRIMARY       | NULL       | NULL    | NULL      |    7 | NULL  |
|  1 | SIMPLE      | a     | ref  | fk_dept_id    | fk_dept_id | 5       | db01.b.id |    1 | NULL  |
+----+-------------+-------+------+---------------+------------+---------+-----------+------+-------+
  1. range:只检索指定范围的行,使用一个索引来选择行
mysql> explain select * from tbl_dept where id between 1 and 3;
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | tbl_dept | range | PRIMARY       | PRIMARY | 4       | NULL |    3 | Using where |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
  1. index:index与all的区别就是只遍历索引树,通常比all快。
mysql> explain select id from tbl_dept;
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | tbl_dept | index | NULL          | PRIMARY | 4       | NULL |    7 | Using index |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
  1. all:查询所有
mysql> explain select * from tbl_emp;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | tbl_emp | ALL  | NULL          | NULL | NULL    | NULL |    8 | NULL  |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+

possible _keys

作用:显示可能用在这张表上的索引,显示涉及到的字段上若存在索引,则该索引将被列出,但不一定被使用

key

作用:实际被使用到的索引

  1. 若存在覆盖索引,则索引值只出现在key中
  2. 覆盖索引:查询的字段与多值索引中的字段一一对应

有覆盖索引

mysql> create index idx_deptName_locAdd on tbl_dept(deptName,locAdd);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select deptName,locAdd from tbl_dept;
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key                 | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-------------+
|  1 | SIMPLE      | tbl_dept | index | NULL          | idx_deptName_locAdd | 216     | NULL |    7 | Using index |
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-------------+

没有覆盖索引

mysql> explain select deptName,locAdd from tbl_dept;
+----+-------------+----------+------+---------------+------+---------+------+------+-------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | tbl_dept | ALL  | NULL          | NULL | NULL    | NULL |    7 | NULL  |
+----+-------------+----------+------+---------------+------+---------+------+------+-------+

key_len

作用:key_ len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_ len是 根据表定义计算而得,不是通过表内检索出的。索引越长越精确,在不损失精确度的情况下索引越短越好。

扫描二维码关注公众号,回复: 4325043 查看本文章
mysql> explain select * from tbl_dept where deptName = 'RD';
+----+-------------+----------+------+---------------------+---------------------+---------+-------+------+--------------------------+
| id | select_type | table    | type | possible_keys       | key                 | key_len | ref   | rows | Extra                    |
+----+-------------+----------+------+---------------------+---------------------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | tbl_dept | ref  | idx_deptName_locAdd | idx_deptName_locAdd | 93      | const |    2 | Using where; Using index |
+----+-------------+----------+------+---------------------+---------------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)

mysql> mysql> explain select * from tbl_dept where deptName = 'RD' and locAdd = '11';
+----+-------------+----------+------+---------------------+---------------------+---------+-------------+------+--------------------------+
| id | select_type | table    | type | possible_keys       | key                 | key_len | ref         | rows | Extra                    |
+----+-------------+----------+------+---------------------+---------------------+---------+-------------+------+--------------------------+
|  1 | SIMPLE      | tbl_dept | ref  | idx_deptName_locAdd | idx_deptName_locAdd | 216     | const,const |    2 | Using where; Using index |
+----+-------------+----------+------+---------------------+---------------------+---------+-------------+------+--------------------------+
1 row in set (0.00 sec)

ref

作用:显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值

mysql> explain select * from tbl_emp a,tbl_dept b where a.deptld = b.id and b.locAdd = '12';
+----+-------------+-------+-------+---------------+---------------------+---------+-----------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key                 | key_len | ref       | rows | Extra                    |
+----+-------------+-------+-------+---------------+---------------------+---------+-----------+------+--------------------------+
|  1 | SIMPLE      | b     | index | PRIMARY       | idx_deptName_locAdd | 216     | NULL      |    7 | Using where; Using index |
|  1 | SIMPLE      | a     | ref   | fk_dept_id    | fk_dept_id          | 5       | db01.b.id |    1 | NULL                     |
+----+-------------+-------+-------+---------------+---------------------+---------+-----------+------+--------------------------+

# db01.b.id表示db01库的b(tbl_dept)表的id字段被引用了

rows

作用:估算查询出结果所需要查询的行数

Extra

作用:包含不适合在其他列显示,但又十分重要的信息

  1. Using filesort(文件内排序)
    说明mysq|会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”。
mysql> create index idx_deptName_locAdd on tbl_dept(deptName,locAdd);
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from tbl_dept order by locAdd;
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-----------------------------+
| id | select_type | table    | type  | possible_keys | key                 | key_len | ref  | rows | Extra                       |
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | tbl_dept | index | NULL          | idx_deptName_locAdd | 216     | NULL |    7 | Using index; Using filesort |
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-----------------------------+
1 row in set (0.01 sec)

mysql> explain select * from tbl_dept order by deptName,locAdd;
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key                 | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-------------+
|  1 | SIMPLE      | tbl_dept | index | NULL          | idx_deptName_locAdd | 216     | NULL |    7 | Using index |
+----+-------------+----------+-------+---------------+---------------------+---------+------+------+-------------+
1 row in set (0.00 sec)

# 对比以上两个结果可以发现,第一个结果中的Extra多了一个Using filesort。出现这个现象的原因是MySQL中无法利用索引完成排序操作。
# 我们创建索引的顺序是deptName_locAdd,但在使用的时候却直接使用了locAdd,跳过了deptName。
# 就像我们造楼梯时的顺序是一楼、二楼、三楼,使用楼梯时却想跳过一楼直接用二楼,显然是不可能的。只能自建楼梯。
  1. Using temprory(使用了临时表保存中间结果)
    使用了临时表保存中间结果,常见于排序order by和分组group by。
mysql> explain select * from tbl_dept group by locAdd;
+----+-------------+----------+-------+---------------------+---------------------+---------+------+------+----------------------------------------------+
| id | select_type | table    | type  | possible_keys       | key                 | key_len | ref  | rows | Extra                                        |
+----+-------------+----------+-------+---------------------+---------------------+---------+------+------+----------------------------------------------+
|  1 | SIMPLE      | tbl_dept | index | idx_deptName_locAdd | idx_deptName_locAdd | 216     | NULL |    7 | Using index; Using temporary; Using filesort |
+----+-------------+----------+-------+---------------------+---------------------+---------+------+------+----------------------------------------------+
1 row in set (0.00 sec)

mysql> mysql> explain select * from tbl_dept group by deptName,locAdd;
+----+-------------+----------+-------+---------------------+---------------------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys       | key                 | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------------+---------------------+---------+------+------+-------------+
|  1 | SIMPLE      | tbl_dept | index | idx_deptName_locAdd | idx_deptName_locAdd | 216     | NULL |    7 | Using index |
+----+-------------+----------+-------+---------------------+---------------------+---------+------+------+-------------+
1 row in set (0.00 sec)
  1. Using index(表示使用了覆盖索引)

    1. 表示相应的select操作中使用了覆盖索引(Covering Index), 避免访问了表的数据行,效率不错!
    2. 如果同时出现using where,表明索引被用来执行索引键值的查找;
    3. 如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
  2. Using where(使用了where查询)

  3. Impossible where(where的值为false)

猜你喜欢

转载自blog.csdn.net/zyp1376308302/article/details/84626727