SQL语句执行方案
在互联网项目中,必然少不了数据库的使用,随着服务器QPS的上升,数据库的性能逐渐成为产品发展的瓶颈,对于快速响应用户的操作请求,掌控SQL语句的执行方案,对于请求的快速响应、查询的性能提升有着举足轻重的作用。
互联网项目中,大部分情况下使用MySQL数据库,查看查询优化器如何执行查询的主要方法是使用 EXPLAIN 命令。
EXPLAIN简介
若要使用EXPLAIN,只需要在查询中的SELECT关键字之前添加 EXPLAIN 即可。MySQL会在查询中设置一个标记,在执行查询时,此标记返回执行计划的每一步信息。如下图所示。
从此简单示例中,可以看到 EXPLAIN 的输出列表,主要包含以下字段:
- id - 查询编号,一般简单SELECT语句,编号为1,如有子查询时,则为顺序编号。
- select_type - 显示SELECT是简单还是复杂。
- table - 查询的数据表名或者表别名。
- partions - 使用EXPLAIN PARTITIONS时出现,查询使用的分区
- type - 查询关联类型
- possible_keys - 查询可能使用的索引
- key - 查询使用的索引
- key_len - 索引使用的字节数
- ref - 查询值所用的列或者常量
- rows - 查询读取的行数
- filtered - 使用 EXPLAIN EXTENDED时出现,显示针对表里符合某个条件的记录数的百分比所做的一个悲观估算
- Extra - 不适合在其他列显示的额外信息
EXPLAIN显示的是近似值,可以从宏观层面大致显示查询的执行步骤、信息。
EXPLAIN使用
1. id
此列包含一个查询编号,标识SELECT所属的行。如果查询中没有子查询或者联合,则只会有唯一一个SELECT,则每行都将显示为 1。否则,内层的SELECT语句则会顺序编号,对应于其在原始语句中的位置。如上图展示一般。
2. select_type
此列显示查询的复杂程度。MySQL将SELECT查询分为简单和复杂类型,复杂类型分为:简单子查询、派生表、UNION查询。
-
如果查询是简单类型,则EXPLAIN标记为SIMPLE,意味着查询不包含子查询和UNION;
-
如果查询是复杂类型,则EXPLAIN最外层部分标记为PRIMARY,而其他部分则为如下部分:
-
SUBQUERY
包含在SELECT列表中的子查询中的SELECT,如下查询中的ID为2的EXPLAIN行。
-
DERIVED
包含在FROM子句的子查询中SELECT,MySQL会递归执行并将结果放到一个临时表中。服务器内部称为派生表,因为临时表是从子查询中派生而来。
-
UNION
在UION查询中的第二个和随后的SELECT被标记为UNION。
-
UNION RESULT
用来从UNION的匿名临时表检索结果的SELECT被标记为UNION RESULT
UNION和UNION RESULT示例:
SUBQUERY和UNION可以被标记为:DEPENDENT、UNCACHEABLE。
-
DEPENDENT
意味着SEELCT依赖于外层查询中发现的数据
-
UNCACHEABLE
意味着SEELCT中的某些特性阻止结果被缓存于一个Item_cache中
-
3. table
此列显示对应行正在访问的数据表,通常情况下为表名或者表的别名。
-
表名或者别名
正常简单查询时,显示表名或者表别名
-
derivedN
表示派生表,其中 N 表示子查询的ID
-
union1,2
在UNION查询时,展示此数据,表示包含UNION 的id 列表
4. type
此列表示MySQL决定如何查找表中的行。按照性能从差到优的排序,依次如下:
1. ALL
MySQL扫描整张表,从头到尾查找需要的行。
2. index
和全表扫描一样,MySQL扫描表按照索引次序进行。
优点
避免了排序
缺点
需要承担按索引次序读取整个表的开销。
意味着若按照随机次序访问行,则查询开销会非常大。
Extra列中看到“Using index”,说明MySQL使用覆盖索引,只扫描索引的数据,而不按索引次数的每一行。比按索引次序全表扫描的开销要少很多。
3. range
范围扫描,即一个有限制的索引扫描,查询开始于索引里的某一点,返回匹配这个值域的行,不用遍历全部的索引。
SQL语句中带有BETWEEN或者WHERE子句里带有>的查询即为范围扫描。
4. ref
索引访问数据,即返回所有匹配某个单个值的行。可能会找到多个符合条件的行,因此是查找和扫描的混合体。
此类索引访问只有当使用非唯一性索引或者唯一性索引的非唯一性前缀时才会发生。
ref_or_null 意味着MySQL必须在初次查找的结果里进行第二次查找以找出NULL条目。
5. eq_ref
此类索引查找,MySQL知道最多只返回一条符合条件的记录。
SQL语句使用主键或者唯一性索引查找时显示。
6. const、system
当MySQL能对查询的某部分进行优化并将其转换成一个常量时,其就会使用此种访问类型,如下:
7. NULL
表示MySQL能够在优化阶段分解查询语句,在执行阶段甚至不用再次访问表或者索引。
5. possible_keys
显示查询过程中可以使用哪些索引,其基于查询访问的列和使用的比较操作符来进行判断。
6. key
显示查询使用哪个索引来优化对该表的访问。如果该索引未出现在possible_keys列中,则MySQL使用它可能是出于其他的原因,如使用了覆盖索引。
如上查询,该查询使用了覆盖索引,key列有值,而possible_keys无值。
7. key_len
显示MySQL在索引里使用的字节数。
计算列的使用情况时,还需将字符列的字符集考虑进去。
考虑下表
explain语句输出如下:
可以看到key_len为47,那么此47是如何获得的?
计算方式如下:【15 * 3 + 2 = 47】
15 为phone列的字符串长度
3 为一个 utf8 字符占用的字节数(3个字节)
2 为varchar类型用于存储字符串长度占用的字节数
8. ref
显示之前的表在key列记录的索引中查找值所用的列或者常量。
9. rows
显示MySQL估计为了找到所需的行而要读取的记录数。此数据只显示可能需要读取的行数而不是从表里读取出来的实际行数。
10. filtered
MySQL5.1版本后新加入的,在使用 EXPLAIN EXTENDED时出现,显示的是针对表里符合某个条件的记录数的百分比所做的一个悲观估算。
将rows列与此百分比相乘,就能看到MySQL估算将查询到的行数。
此列主要是MySQL在做范围查询时需要消耗的成本,MySQL在某些查询时,会根据此值判断使用索引或者全表扫描或者其他策略。
11. Extra
包含不适合在其他列显示的额外信息。
常见的信息如下:
Using index
表示查询使用覆盖索引,避免访问表。
Using where
表示MySQL服务器将在存储引擎检索行后在进行过滤。
Using temporary
表示MySQL对查询结果排序时使用了临时表。临时表可能存在内存或者磁盘中。
Using filesort
表示MySQL对结果使用一个外部索引排序,而不是按索引次序从表中读取行。当无法使用索引生成排序结果时,MySQL需要自己进行排序,根据数据量,MySQL会在不同场景进行数据排序:
- 内存,数据量较小时,直接在内存中进行排序
- 磁盘,数据量较大时,使用磁盘进行排序
不管排序的场所在何处,MySQL将此过程统一称为文件排序,即filesort。同时,EXPLAIN也不会告知使用何种排序算法。
Range checked for each record(index map: N)
此意味着没有好用的索引,新的索引将在联接的每一行上重新估算。N 是显示在 possible_keys 列中索引的位图,并且是冗余的。
EXPLAIN限制
使用EXPLAIN能从宏观层面反映出SQL语句的执行方案,业务中可根据此执行方案进行优化,提升SQL执行效率、进而可以提升整体业务的QPS。但是EXPLAIN也有一定的自身限制,如:
- EXPLAIN不会解释触发器、存储过程或者UDF的执行方案;
- UDF是mysql的一个拓展接口,UDF(Userdefined function)可翻译为用户自定义函数,这个是用来拓展Mysql的技术手段。
- EXPLAIN 不会提供MySQL在查询执行中所做的特定优化;
- 不区分具有相同名字的事物,如filesort、Using temporary,并不区分查询发生在内存还是磁盘中;
- 可能存在一定的误导,如对于limit语句的查询显示全索引扫描。
虽然EXPLAIN存在以上一些限制,并不总会说出真相,但是其输出是可以获取到的最好的信息,也有助于了解MySQL优化器的工作流程。
[延伸阅读]