记一次线上慢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的这个执行计划,可能要更多地在实践中摸索。
~
正文到此结束
热门推荐
相关文章
广告是为了更好的提供数据服务