当我们优化一个低性能的SQL时,通常第一件事就是查看执行计划。通过执行计划我们可以查看MySQL是如何处理一个SQL语句的,包括表的连接顺序、预估成本、索引使用情况等。
根据SQL执行计划可以找到性能低下的瓶颈在哪,在不需要重写SQL的情况下,通常SQL性能问题可以分为以下几类:
对应的解决方法通常为:
目录
一、查看执行计划
1. explain
2. explain extended
3. explain partitions
二、执行计划输出列含义
1. id
2. select_type
3. table_name
4. type
5. possible_keys
6. keys
7. key_len
8.ref
9. rows
10. filtered
11.Extra
查看执行计划,最简单的办法就是在SQL前添加explain关键字,explain不仅可以解释select查询,还可以解释delete,insert,update和replace语句,但最常用的场景还是优化select查询:
explain select * from cor_crm_ent;
在explain后增加extended关键字,mysql会在执行计划中额外增加一列filtered,并且会根据执行计划逆向编译出一个select语句。这个语句是在生成执行计划后,根据执行计划反向编译出来的,与原SQL无关,可以用show warnings语句查看。大部分情况下这个SQL与原SQL不同,可以通过这种方式查看mysql是如何改写SQL语句的。
explain extended select cust_id,company_name from cor_crm_ent;
注意explain extended语句会附带一个warning信息,通过show warnings我们可以查看反向编译的SQL:
show warnings;
explain partions可以显示检索的分区信息,这个选项只有在涉及分区表才会使用,普通非分区表即使使用也显示为空
explain partitions select count(*) from focs_interest partition (p_201808);
select的标识符,一般按select出现的顺序编号。但如果是引用union的结果,id显示为空,此时table列会显示
explain
select * from ( -- id为1的select
select cust_id from cor_crm_ent -- id为2的select
union
select credit_id from cor_lms_facility) t; -- id为3的select
注意最后一行union result,id的编号为空,同时table列是
select_type代表不同的select类型,具体的值和含义如下表:
select_type | 含义 |
SIMPLE | 简单select查询(没有用到union或子查询) |
PRIMARY | 最外层select。这个类型通常可以在DERIVED和UNION类型混合使用时见到。 |
UNION | union中第二个以及之后的select |
DEPENDENT UNION | union中第二个以及之后的select,且与外层查询关联 |
UNION RESULT | union的结果 |
SUBQUERY | 子查询中第一个select |
DEPENDENT SUBQUERY | 子查询中第一个select,且与外层查询有关联 |
DERIVED | 出现在FROM列表中的子查询被标记为DERIVED(衍生表) |
MATERIALIZED | 物化子查询 |
UNCACHEABLE SUBQUERY | 无法缓存的子查询,外层查询的每一行都要重新计算子查询的值 |
UNCACHEABLE UNION | UNCACHEABLE SUBQUERY中union查询第二及之后的select |
查询用到的表名,也可能是union结果集,衍生表,物化子查询,可能出现的值如下:
table_name | 含义 |
table_name | 查询引用的表名 |
id为M和N查询union后的结果 | |
id为N的衍生表 | |
id为N的物化子查询 |
数据的访问方法,具体的解释可以参考下面文章:
MySQL查询优化之一:数据的访问方法(Access Paths)_V1ncent Chen的博客-CSDN博客
查询中可能用到的索引,这里只是列出可以使用的索引,但实际查询时并不一定会用到。
显示MySQL实际使用的索引,如果这个索引没有出现在possible_keys列中,那么MySQL选用它是出于另外的原因——比如它可能选择了一个覆盖索引。
实际使用的索引(keys列)的字节数。
显示在key列记录的索引中查找值所用的列或常量。
这一列是MySQL估计为了找到所需的行而要读取的行数。数值根据索引和统计信息计算出,可能不是很精确。rows列的值如果很大,通常代表访问方法(type列,access paths)不是很高效,需要尝试是否有更高效的访问方法。
使用explain extended会显示此列,它显示的是针对表里符合某个条件(WHERE子句或联接条件)的记录数的百分比所做的一个估算。如果你把rows列和这个百分比相乘,就能算出MySQL估算它将和查询计划里前一个表关联的行数。
在查看执行计划时,rows*filtered列是一个需要关注的重点,大量的数据会导致连接速度急剧下降。
包含一些不适合在其他列显示额外信息,其中比较重要的有如下(完整的内容较多,可以参考官方文档):
Extra | 含义 |
Using where | MySQL服务器在收到存储引擎的记录后通过where条件进行“后过滤”(MySQL中索引过滤是在存储引擎层,where条件过滤是在应用层) |
Using temporary | MySQL将使用临时表来处理结果集,常见于排序和分组查询 (如果无法在内存中建立临时表,建立磁盘临时表成本会很高) |
Using filesort | MySQL无法利用索引来完成的排序操作称为filesort(这是一个CPU密集型操作) 可以通过建立合适的索引来消除排序 |
Using index | 索引覆盖,只需要访问索引就可以获取结果,无需回表操作 |
Using join buffer | 连接时没有使用索引,需要连接缓冲区来存储中间结果 考虑添加合适的索引来改进性能 |
Impossible where | 没有符合where条件的记录 |
Using MRR | 使用MRR(Multi-Range Read)优化策略(二级索引随机I/O转化为顺序I/O) |
在优化SQL,阅读执行计划时,我们通常需要关注的点有:
对于也SQL可以尝试优化改写,并查看不同的执行计划区别,找到最高效的方式。