原创

记一次线上慢SQL 查询的优化过程

现象

最近线上服务卡顿频发, 原因除了部分时间段出现 FGC,还有一个重要因素就是存在部分SQL查询时间均在秒级之上。

SQL example:tableC

SELECT * FROM   tableC 
WHERE (modify_time >= '2020-12-16 02:00:00' AND modify_time <= '2020-12-16 03:00:00') 
GROUP BY rent_code,operator_id ORDER BY create_time DESC;

查询结果: 【消息】:执行成功,当前返回:[2]行,耗时:[959]ms.

SQL诊断

查看执行计划

MySQL 使用 explain + sql 语句查看 执行计划,该执行计划不一定完全正确但是可以参考。

explain SELECT * FROM tag_asset_log 
WHERE (modify_time >= '2020-12-16 02:00:00' AND modify_time <= '2020-12-16 03:00:00') 
GROUP BY rent_code,operator_id ORDER BY create_time DESC;
+--------------+-----------------------+-----------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+----------------------------------------------+

| id           | select_type           | table           | type           | possible_keys           | key           | key_len           | ref           | rows           | Extra                                        |

+--------------+-----------------------+-----------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+----------------------------------------------+

| 1            | SIMPLE                | tag_asset_log   | ALL            |                         |               |                   |               | 1090788        | Using where; Using temporary; Using filesort |

+--------------+-----------------------+-----------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+----------------------------------------------+

返回行数:[1],耗时:0 ms.

执行计划中各字段说明:

select_type

select_type字段 描述
SIMPLE 简单查询
PRIMARY 最外层查询
SUBQUERY 映射为子查询
DERIVED 子查询
UNION 联合
UNION RESULT 使用联合的结果

table

正在访问的表名

type

type 字段 描述
ALL 全数据表扫描
index 全索引表扫描
RANGE 对索引列进行范围查找
INDEX_MERGE 合并索引,使用多个单列索引搜索
REF 根据索引查找一个或多个值
EQ_REF 搜索时使用primary key 或 unique类型
CONST 常量,表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次。
SYSTEM 系统,表仅有一行(=系统表)。这是const联接类型的一个特例。

性能(从低到高):all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
性能在 range 之下基本都可以进行调优(all , index)

extra

使用的索引方式

extra 字段 描述
Using index 此值表示mysql将使用覆盖索引,以避免访问表,无须回表。
Using where Using index 查询的列被索引覆盖,并且where筛选条件是索引列之一但是不是索引的不是前导列。此时意味着无法直接通过索引查找来查询到符合条件的数据,通过索引扫描(或者表扫描)来实现sql语句执行
Using where mysql 将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不同的索引。该extra字段的出现,意味着将通过索引或者表扫描的方式进程where条件的过滤,反过来说,也就是没有可用的索引查找,当然这里也要考虑索引扫描+回表与表扫描的代价。
Using index condition 查询列的某一部分无法直接使用索引
Using temporary mysql 对查询结果排序时会使用临时表。
Using filesort mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。
Range checked for each record(index map: N) 没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的
NULL 没有好用的索引,意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来实现,不是纯粹地用到了索引,也不是完全没用到索引,Extra中为NULL(没有信息)

possible_keys

可能使用的索引

key

真实使用的索引

key_len

MySQL中使用索引字节长度

rows

mysql 预估为了找到所需的行而要读取的行数

SQL 优化

根据上述的诊断信息,我们可以判断没有走到索引。因此先增加索引操作。
tableC DDL 语句

ALTER TABLE   `tableC` ADD INDEX `idx_modifytime` (`modify_time`);

优化后

tableC

再次执行 sql, 执行结果: 【消息】:执行成功,当前返回:[2]行,耗时:[1]ms.
性能提升900倍!

再看执行计划

explain SELECT * FROM tag_asset_log 
WHERE (modify_time >= '2020-12-16 02:00:00' AND modify_time <= '2020-12-16 03:00:00') 
GROUP BY rent_code,operator_id ORDER BY create_time DESC;

+--------------+-----------------------+-----------------+----------------+-------------------------+----------------+-------------------+---------------+----------------+--------------------------------------------------------+

| id           | select_type           | table           | type           | possible_keys           | key            | key_len           | ref           | rows           | Extra                                                  |

+--------------+-----------------------+-----------------+----------------+-------------------------+----------------+-------------------+---------------+----------------+--------------------------------------------------------+

| 1            | SIMPLE                | tag_asset_log   | range          | idx_modifytime          | idx_modifytime | 5                 |               | 102            | Using index condition; Using temporary; Using filesort |

+--------------+-----------------------+-----------------+----------------+-------------------------+----------------+-------------------+---------------+----------------+--------------------------------------------------------+

总结:

MySQL 执行计划中的Extra中信息非常多,不仅仅包括Using index,Using where Using index,Using index condition,Using where,尤其是在多表连接的时候,这一点在相对MSSQL来说,不够直观或者结构化。

MySQL 中是通过区分索引查找(index seek),索引扫描(index scan),表扫描(table scan)来实现具体的查询的,这图形化的执行计划在不同的场景下是非常直观的,要想完全弄懂MySQL的这个执行计划,可能要更多地在实践中摸索。

~

正文到此结束
广告是为了更好的提供数据服务
本文目录