数据库执行计划是优化SQL时必备的技能,如果只靠猜和借鉴前人的经验,只会让自己不断的踩坑。
1. 索引了解一下
在写执行计划之前需要先了解一下数据库有哪些索引。
- 普通索引:建立在普通的单列上,单纯的索引列,没有唯一性的要求。
- 组合索引:在表中的多个字段组合创建的索引。
- 唯一索引:当前索引列的值是唯一的,不可重复的。
- 全文索引:全文索引如同普通索引的细化,只能在char、varchar、text数值类型类中建立。
其中组合索引有一个特性就是最左前缀原则,组合索引的最左列被使用到才会让整个组合索引生效。比如一个索引idx_name_phone,在索引列中name在前面,phone在后,当单纯是使用phone来查询的时候,是不会走idx_name_phone索引的,只有在name被使用的时候,才会让整个索引起作用。
全文索引只能在MyISAM存储引擎里使用,其他引擎不支持,他的特点如下:
- 默认只支持英文,不支持中文,需要借助第三方工具sphinx和coreseek来实现
- 只在MyISAM存储引擎中适用,其他存储引擎不支持
- 语法不同其他索引,需要使用指定的match against语法,如下示例:
//其中title和content是建立了全文索引的列
select * from atricle where match (title,content) against ("mysql");
另外还有两种索引的存储结构,分别是聚集索引和非聚集索引。
- 非聚集索引,索引文件和数据存储文件是分别的两个文件。此索引又被成为非聚簇索引
- 聚集索引,索引文件和数据存储在同一个文件中,此索引又被称为聚簇索引
这两种索引其实准确的说是两种索引存储结构,和上面的几种索引是有本质上的区别。这两种索引在理解MySQL的MyISAM存储引擎和InnoDB存储引擎在B+Tree的时候需要,以后的文章会有体现,这里就不多写了,作为一个了解。
最后需要了解一个索引上常说的一个概念,叫做覆盖索引,听起来又是一种索引,但实际上不是,从语法上可以理解为索引的覆盖。表示在条件中使用到索引列,并且同时索引列也在需要查询的列中,此时就会使用到覆盖索引。但是需要注意的是只有BTree会存储索引列,其他的如哈希索引、空间索引等都不存储。因此可以理解为MySQL中只能对使用BTree数据结构的存储引擎适用。
在SQL优化的时候会经常用到覆盖索引,可以解决大部分索引失效的情况,以后在写SQL优化的时候再具体说。
2. 执行计划
2.1 概念
想要看执行计划,只需要在SQL语句的前面加上explain关键字即可。执行结果有可能是多行,但是列基本是固定的。
所拥有的基本列如下:
2.2 id
包含一组数字,表示查询中执行select子句或操作表的顺序。
这里会出现三种情况:
- 多行执行计划的id相同,这个时候表示自上而下依次执行
- 多行执行计划的id都不相同,id的值越大表示优先级越高,越早被执行
- 多行执行计划的id有相同有不同,此时就按照id来分组,同一组按照自上而下依次执行,不同组根据id的值来决定执行顺序,id越大优先级越高,越早被执行
2.3 select_type
表示多个select字句的类型,主要的类型有下面几种:
- simple:查询语句中不包含子查询或者union查询
- primary:查询中若包含任何复杂的子部分,最外层查询则被标记为:primary
- subQuery:select或where列表中包含了子查询,该子查询被标记为:subQuery
- derived:在from列表中包含的子查询被标记为:derived,表示衍生的意思
- 若第二个select出现在union之后,则被标记为union;若union包含在from子句的子查询中,外层select将被标记为:derived
- 从union表获取结果的select被标记为:union result
2.4 type
表示MySQL在表中找到所需行的方式,又称“访问类型”。常见的类型有:ALL、index、range、ref、eq_ref、const、system。从左到右,由最差到最好。
- ALL:full table scan,全表扫描
- index:index表示使用了索引,但缺点是扫描的是全表索引
- range:索引范围扫描,对索引的扫描开始于某一点、结束于某一点,返回匹配值域的行,常见于between、in、<、>等的查询
- ref:非唯一性索引扫描,返回匹配某个单独值的所有行。
- eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
- const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键或者唯一索引置于where列表中,MySQL就能将该查询转换为一个常量
2.5 possible_keys
指出MySQL可能使用到的索引,但是实际上有没有使用时不确定的。另外如果此列的值为null,也有可能走了索引。因此这个列只作为一个参考值,主要是看下面的key列。
2.6 key
显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。
2.7 key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。
当是联合索引的时候,如果多列中只有其中几列使用到,对应key_len长度就是这几列的总长度之和。可以用来判断哪些条件用到索引,哪些条件没有用到索引,或者索引是否失效。
这里需要了解的是各种类型长度的计算,因为这个长度并不是在创建表时指定的长度(L)。
- 当是char的时候,长度是Lx3,另外如果该列可以为空,需要在计算的结果上加一
- 当是varchar的时候,长度是Lx3+2,另外如果该列可以为空,需要在计算的结果上加一
- 当是int类型的时候,固定长度为4,如果可以为空,就加一
- 当是datetime类型的时候,在MySQL5.6版本之前是固定长度4,之后的版本固定长度为8,如果可以为空,加一即可
- 当是timestamp类型的时候,固定长度为4,如果可以为空,就加一
图中的user_name(长度32)和user_phone(长度11)是建立的联合索引,通过key_len可以看的出来,当只使用user_name查询的时候,索引长度是98,当两个都作为查询条件的时候,索引长度为133。
2.8 ref
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。
在第二行中,ref就明确的指出使用了具体那一列。
2.9 rows
表示MySQL根据表统计信息及索引选用情况,估算找到所需的记录所需要读取(扫描)的行数。这个很好理解。
2.10 extra
包含不适合在其他列中显示但十分重要的额外信息
- Using index:该值表示相应的select操作中使用了覆盖索引,如果没有使用覆盖索引对应的值是Using index condition。
注意:如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *
,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。
- Using where:表示MySQL服务器在存储引擎收到记录后进行“后过滤”(Post-filter),如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集。
- Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询
- Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”(分组也是一样)
上面的Using temporary图中就涉及到Using filesort,因为user_name和user_phone是联合索引,只使用user_phone不符合最左前缀原则,因此不能实现按索引分组,使用了文件分组。在后期的实验中,发现普通索引也是不能实现按索引分组或者排序。
3. MySQL执行计划的局限
- EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
- EXPLAIN不考虑各种Cache
- EXPLAIN不能显示MySQL在执行查询时所作的优化工作
- 部分统计信息是估算的,并非精确值
- EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划