是什么
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。
Mysql常见瓶颈
EXPLAN能干什么
- 表的读取顺序:体现在id上
- 数据读取操作的操作类型:体现在select_type上
- 那些索引可以使用:体现在possible_keys
- 那些索引被实际使用:体现在key上
- 表之间的引用:
- 每张表有多少行被优化器查询:体现在rows上
EXPLAN怎么用
用法:explain+sql语句
执行计划包含的信息
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
ID
作用:体现了表的读取顺序
- id相同:按顺序读取
- id不同:从大到小进行读取
- 有的id相同,有的id不同:先从大到小读取,相同的按顺序读取
select_type
作用:标记了查询的类型
- 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 |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
- 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 |
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
- 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 |
+----+-------------+-------+------+---------------+------------+---------+-------+------+-------------+
- 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 |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
- 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 |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
- 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
- system:表只有一行记录(等于系统表),平时不会出现,可以忽略不计
- 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 |
+----+-------------+----------+-------+---------------+---------+---------+-------+------+-------+
- 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 |
+----+-------------+-------+-------+---------------+------------+---------+-------+------+-------+
- 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 |
+----+-------------+-------+------+---------------+------------+---------+-----------+------+-------+
- 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 |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
- 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 |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
- 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
作用:实际被使用到的索引
- 若存在覆盖索引,则索引值只出现在key中
- 覆盖索引:查询的字段与多值索引中的字段一一对应
有覆盖索引
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是 根据表定义计算而得,不是通过表内检索出的。索引越长越精确,在不损失精确度的情况下索引越短越好。
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
作用:包含不适合在其他列显示,但又十分重要的信息
- 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。
# 就像我们造楼梯时的顺序是一楼、二楼、三楼,使用楼梯时却想跳过一楼直接用二楼,显然是不可能的。只能自建楼梯。
- 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)
-
Using index(表示使用了覆盖索引)
- 表示相应的select操作中使用了覆盖索引(Covering Index), 避免访问了表的数据行,效率不错!
- 如果同时出现using where,表明索引被用来执行索引键值的查找;
- 如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
-
Using where(使用了where查询)
-
Impossible where(where的值为false)