从一个实例分析order by 优化

本文通过一个实例分析了MySQL中ORDER BY查询的优化问题。在一张3000万数据的表上,一个简单的带有ORDER BY和LIMIT的SQL查询在选择索引时出现性能差异。优化器选择了index_merge而非覆盖索引,导致性能下降。通过对比发现,覆盖索引idx_toplevel_leftuserid_rightuserid能显著提高查询速度。当遇到类似情况,使用索引提示可以避免全表扫描,提高查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

环境:

MySQL5.7.30

简介:

生产环境一张读压力很大的表,表数据量在3000万左右,该表主要是一条很简单的sql在查,因时入慢日志时间为1s,初期该sql并未进入到慢日志,一次引流导致出现了大量sql未走索引,活跃会话数激增,CPU瞬间打满的情况,由此引发了对优化器在索引选择上的一些思考。

建表语句如下:

因为生产环境,所以这里匿去表名,只分析原因,部分地方用test1代替:

CREATE TABLE `test1` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`relation_code` varchar(64) DEFAULT NULL,

`left_user_id` bigint(20) DEFAULT NULL,

`right_user_id` bigint(20) DEFAULT NULL,

`top_level` tinyint(4) NOT NULL ,

`relation_type` tinyint(4) NOT NULL DEFAULT '1' ,

`update_time` timestamp NULL DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `uni_relation_code` (`relation_code`),

KEY `idx_left_user_id` (`left_user_id`),

KEY `idx_right_user_id` (`right_user_id`),

KEY `idx_toplevel_leftuserid_rightuserid` (`top_level`,`left_user_id`,`right_user_id`)

) ENGINE=InnoDB ;

查询接口对应的sql:

SELECT `top_level`

FROM `test1`

WHERE left_user_id = ?

OR right_user_id = ?

ORDER BY `top_level` DESC

LIMIT ? 

从表结构可以看出,该sql就是一条很简单的常量 + order by ,对应可用的索引可以用:

1. index_merge(idx_left_user_id,idx_right_user_id)

但使用该方式在合并了索引过滤出来的数据后,会产生一次file sort,进行一次回表操作

2. idx_toplevel_leftuserid_rightuserid

该索引为一条覆盖索引,从order by 的字段上开始过滤符合条件的数据,达到limit的条数后直接返回结果,从分析上看,所有操作在索引中就可以完成,不需要回表操作,似乎比较符合。

从explain的结果来看:

 实际上优化器选择的是index_merge。

先看一下使用两条索引时对应的查询耗时:

1. 使用 index_merge 时:

 2. 使用 idx_toplevel_leftuserid_rightuserid 时:

可以看到,使用覆盖索引比使用index_merge快10倍以上,至于优化器为什么选择的是index_merge,而不是覆盖索引,下一篇文章咱们再分析,这里先分析为什么引流的时候会导致部分查询走全表扫描,导致数据库异常。

分析问题:

order by/ group by {字段} + limit {条数} 这类的查询优化器会走 {字段} 为前缀的索引,然后扫描where条件的行,凑足limit的行数后直接返回,不再继续往下扫描,所以这里优化器理论上应该选择覆盖索引(idx_toplevel_leftuserid_rightuserid),实际上从执行耗时来看,也确实是这样,但实际上该查询方式会导致查询效率不稳定,可能导致有的sql执行很快,有的sql执行很慢,甚至是全表扫描的情况发生。

解决方式:

使用hint,加上强制索引 idx_toplevel_leftuserid_rightuserid 可以避免全表扫描的情况发生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FightingFreedom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值