上篇文章主要介绍了MySQL索引的原理和结构,其实MySQL的索引就是一颗B+Tree的数据结构,其本质就是通过不断缩小想要获取数据的范围来筛选出想要获取的结果,同时把随机事件变成了有序事件,这篇文章主要介绍explain关键字的使用和MySQL索引优化。
1.MySQL中有专门负责优化SELECT语句的优化器模块,主要功能,通过计算分析系统中收集到统计信息,为客户端请求的Query提供他认为最优的执行计划(他认为最优的数据检索方式,但不见得DBA认为是最优的,这部分最耗费时间)。
2.当客户端向MySQL请求一条Query,命令解析器模块完成请求分类,区别出是SELECT并转发给MySQL Query Optimize时,MySQL Query Optimize首先会对整条Query进行优化,处理条一些常量表达式的预算,直接换算成常量值,并对Query中的查询条件进行简化和转换,如去掉一些无用或显而易见的条件、结构调整等。然后分析Query中的Hint信息(如果有),看显示Hint信息是否可以完全确定该Query的执行计划,如果没有Hint或Hint信息还不足以完全确定执行计划,则会读取锁涉及对象的统计信息,根据Query进行写相应的计算分析,然后再得出最后的执行计划。
(1)可以查看表的读取顺序;
(2)可以查看数据读取操作的操作类型;
(3)可以查看哪些索引可以使用;
(4)可以查看哪些索引被实际使用;
(5)可以查看表与表之间的引用关系;
(6)可以查看每张表有多少行记录被优化器查询。
explain + SQL语句
id是select查询的序列号,是一组数字,表示的是查询中执行select子句或者操作表的执行顺序,有三种情况:id相同、id不同、id相同又不同。
(1)id相同的情况下,数据表的执行顺序由上往下依次执行;
explain select t2.* from t1,t2,t3 where t1.id = t2.id and t1.id = t3.id and t1.other_column = '';
(2)id不同的情况下,如果是子查询,id的序号递增,id值越大优先级越高,越先被执行;
explain select t2.* from t2 where id = (select id from t1 where id = (select t3.id from t3 where t3.other_column = ''));
(3)id相同又不同的情况下,id如果相同,可以认为是一组,从上往下执行,在所有组中,id值越大,优先级越高,越先被执行;
explain select t2.* from (select t3.id from t3 where t3.other_column = '') s1,t2 where s1.id = t2.id;
(1)select_type的类型主要有:SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT
1)SIMPLE:简单的select查询,查询中不包含subquery或者union;
2)PRIMARY:查询中若包含复杂的子查询,最外层查询则被标记为PRIMARY;
3)SUBQUERY:在select中或者where列表中包含的子查询;
4)DERIVED:在from列表中包含的子查询被标记为DERIVED(衍生)MySQL会递归执行这些子查询,把结果放在临时表;
5)UNION:若第二个select出现在UNION后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED;
6)UNION RESULT:从UNION临时表检索结果的select;
(2)select_type的作用:主要用于区分是否是子查询、联合查询、子查询等的复杂查询;
用于显示查询记录的关于哪张表的。
type主要有:ALL、INDEX、RANGE、REF、EQ_REF、CONST、SYSTEM、NULL这几种值;
以上值从最好到最坏依次是:system>const>eq_ref>ref>range>index>all
1)system:表中只有一行记录(等于系统表),这就是const类型的特例,平时不会出现;
mysql> explain extended select * from (select * from film where id = 1) tmp;
+----+-------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
| 1 | PRIMARY | | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| 2 | DERIVED | film | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
mysql> show warnings;
+-------+------+---------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select '1' AS `id`,'film1' AS `name` from dual |
+-------+------+---------------------------------------------------------------+
2)const:表示通过索引一次就可以找到,const用于比较primary key或者unique索引,因为只匹配一行数据,所以很快。如果主键置于where列表中,MySQL就能将查询转换成一个常量;
3)eq_ref:唯一性索引扫描,对于每个索引值,表中只有一条记录与之匹配,常用于主键或唯一索引扫描。
mysql> explain select * from film_actor left join film on film_actor.film_id = film.id;
+----+-------------+------------+--------+---------------+-------------------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+-------------------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | film_actor | index | NULL | idx_film_actor_id | 8 | NULL | 3 | Using index |
| 1 | SIMPLE | film | eq_ref | PRIMARY | PRIMARY | 4 | test.film_actor.film_id | 1 | NULL |
+----+-------------+------------+--------+---------------+-------------------+---------+-------------------------+------+-------------+
4)ref:非唯一性扫描,返回匹配某个结果单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体;
1. 简单 select 查询,name是普通索引(非唯一索引)
mysql> explain select * from film where name = "film1";
+----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+
| 1 | SIMPLE | film | ref | idx_name | idx_name | 33 | const | 1 | Using where; Using index |
+----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+
2.关联表查询,idx_film_actor_id是film_id和actor_id的联合索引,这里使用到了film_actor的左边前缀film_id部分。
mysql> explain select * from film left join film_actor on film.id = film_actor.film_id;
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+-------------+
| 1 | SIMPLE | film | index | NULL | idx_name | 33 | NULL | 3 | Using index |
| 1 | SIMPLE | film_actor | ref | idx_film_actor_id | idx_film_actor_id | 4 | test.film.id | 1 | Using index |
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+-------------+
5)range:只检索指定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等查询,这种范围扫描索引比全表扫描要好,因为只需开始索引的某一点,而结束另一点,不用扫描全部索引。
mysql> explain select * from actor where id > 1;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | actor | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
6)index:Full Index Scan,index与all的区别就是index类型只遍历索引树,这通常比all要快,因为索引文件通常比数据文件要小(也就是说虽然all和index都是全表扫描,但index是从索引中读取,而all是从磁盘中读取)。
mysql> explain select count(*) from film;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| 1 | SIMPLE | film | index | NULL | idx_name | 33 | NULL | 3 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
possible_keys:显示可能应用到这张表的索引,有一个或者多个;查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际用到。
key:实际使用的索引,如果该值为NULL,则没有使用索引;如果查询中使用了覆盖索引,则该索引仅出现在key列表中。
覆盖索引:就是select后面的字段都具备索引,提高了查询效率,前提顺序、个数都要一致;
理解方式一:就是select的数据列只用从索引中就能够获取到,不必读取没有必要多余的数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建索引覆盖。
理解方式二:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个数据行,毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。
使用覆盖索引注意:
如果使用覆盖索引,一定注意select列表中只取需要的列,不可使用select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。
表说索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失的精度性的情况下,长度越短越好。key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表计算定义而得,不是通过表内检索出的。
mysql> explain select * from film_actor where film_id = 2;
+----+-------------+------------+------+-------------------+-------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+-------------------+-------------------+---------+-------+------+-------------+
| 1 | SIMPLE | film_actor | ref | idx_film_actor_id | idx_film_actor_id | 4 | const | 1 | Using index |
+----+-------------+------------+------+-------------------+-------------------+---------+-------+------+-------------+
显示索引的那一列被使用了,如果可能的话,是一个常数(const),哪些列或常量被用于查找索引列上的值;
根据表统计信息及索引选用情况,大致估算出找到所需的记录需要读取的行数。
(1)Using filesort:说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,MySQL中无法利用索引完成的排序操作称为"文件排序";
(2)Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表,常出现在order by排序和group by分组查询中;
(3)Using index:表示相应的select操作中使用了覆盖索引(Covering index),避免了访问了表的数据行,效率不错。如果同时出现了Using where,表明索引被用来执行索引键值的查找;如果没有同时出现Using where,表明索引用来读取数据而非执行查找动作;
(4)Using where:表示查询使用了where条件;
(5)Using join buffer:表示查询使用了连接缓存;
(6)impossible where:where子句的值总是false,不能用来获取任何元祖;
(7)select tables optimized away:在没有group by 子句的情况下,基于索引优化min、max操作或者对于MyISAM存储引擎优化count(*)操作,不必到执行阶段在进行计算,查询执行计划生成的阶段即完成优化。
(8)ditinct:优化distinct操作,在找到第一匹配的元素即停止找同样的动作;