慢查询
慢查询指的是查询速度慢的查询语句,MySQL提供了慢查询日志功能,可以记录查询速度慢的查询语句,以便后续优化。
使用下列命令查看慢查询日志是否开启
1 | show variables like "slow_query_log"; |
slow query log表示慢查询开启的状态
slow_query_log_file表示慢查询日志存放的位置
如果没有开启慢查询日志,可以使用下列命令开启
1 | set global slow_query_log = ON; |
使用show variables like 'long_query_time'
命令,查看超过多少时间,才记录慢查询日志
可以使用set global long_query_time = 1;
设置超过1秒的查询记录慢查询日志,单位是秒,可以自己设置,0表示所有查询都记录慢查询日志。
可以通过上面两个命令结合,查看慢查询SQL的位置,以及慢查询日志记录的时间。
当定位出查询效率低的SQL后,可以使用explain查看SQL的执行计划。
- type:连接类型,查看索引执行情况的一个重要指标。 system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
- system:这种类型要求数据库表中只有一条数据,是const类型的一个特例,一般情况下是不会出现的。
- const:通过一次索引就能找到数据,一般用于主键或唯一索引作为条件,这类扫描效率极高,,速度非常快。
- eq_ref:常用于主键或唯一索引扫描,一般指使用主键的关联查询
- ref : 常用于非主键和唯一索引扫描。
- ref_or_null:这种连接类型类似于ref,区别在于MySQL会额外搜索包含NULL值的行
- index_merge:使用了索引合并优化方法,查询使用了两个以上的索引。
- unique_subquery:类似于eq_ref,条件用了in子查询
- index_subquery:区别于unique_subquery,用于非唯一索引,可以返回重复值。
- range:常用于范围查询,比如:between … and 或 In 等操作
- index:全索引扫描
- ALL:全表扫描
- rows:找到我们所需的记录,需要读取的行数,对于InnoDB表,这个值是一个估计值
- filtered:百分比,表里符合条件的记录树的百分比。表示存储引擎返回的数据在经过过滤后,剩下的满足条件的记录数量的比例
- extra:包含MySQL解决查询的详细信息,常见的值有:
- Using filesort:MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,这个过程称为“文件排序”
- Using temporary:MySQL需要创建一个临时表来存储结果集,常见于排序和分组查询
- Using index:表示相应的select操作中使用了覆盖索引,避免访问了表的数据行,效率较高
- Using where:表示MySQL服务器从存储引擎中获取记录后再进行过滤,这是一个非常好的标志,表示MySQL服务器在存储引擎检索行后,再进行条件过滤,而不是取出所有数据后在服务器层过滤
- Using join buffer:表示使用了连接缓存
- Using index condition:表示使用了索引过滤数据
- key:显示MySQL实际使用的索引,如果为null,则没有使用索引
- key_len:表示对应的执行计划在执行时,使用到的索引字段长度,一般情况下都为索引字段的长度
看执行计划的话,先看select_type、type,其次看rows,再看key、key_len,然后看ref,最后看Extra、filtered,然后结合各项指标,就能推断出谁快谁慢。
explain只是看到SQL的预估执行计划,如果要了解SQL真正的执行线程状态以及消耗的时间,需要使用profiling,profiling: 分析执行耗时,默认是关闭,可以使用show variables like '%profil%'
查看是否开启。开启profiling参数后,后续执行的SQL语句都会记录其资源开销,包括IO,上下文切换,CPU,内存等等,我们可以根据这些开销进一步分析当前慢SQL的瓶颈再进一步进行优化。
1 | set profiling = 1; -- 开启profiling |
profile只能查看SQL的执行耗时,无法查看SQL真正执行的过程信息,我们可以使用Optimizer Trace,跟踪执行语句的解析优化执行的全过程。可以使用set optimizer_trace = "enabled=on"
开启,输入一条SQL语句,然后使用select * from information_schema.OPTIMIZER_TRACE
查看那条SQL的执行过程。
可以看到三个阶段,join_preparation
表示准备阶段,join_optimization
表示分析阶段,join_execution
表示执行阶段。
最后确认问题,采取措施
- 多数慢SQL都跟索引有关,比如不加索引,索引不生效、不合理等,这时候,我们可以优化索引。
- 我们还可以优化SQL语句,比如一些in元素过多问题(分批),深分页问题(基于上一次数据过滤等),进行时间分段查询
- SQl没办法很好优化,可以改用ES的方式,或者数仓。
- 如果单表数据量过大导致慢查询,则可以考虑分库分表
- 如果数据库在刷脏页导致慢查询,考虑是否可以优化一些参数,跟DBA讨论优化方案
- 如果存量数据量太大,考虑是否可以让部分数据归档