文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

准线上事故之MySQL优化器索引选错

2024-11-30 00:33

关注

2 过程和分析

2.1 问题发现

20年的某个下午,突然收到大量慢查询的告警,同时业务运营在群里反馈红包相关页面加载慢,怀疑系统出问题了,问题发到群里之后,经过日志定位和代码review多重确认,有一条sql成了重点怀疑对象,最终确定的原因是MySQL查询过程中,优化器没有选择最优的索引导致的。

图片

需要说明的是,这里使用的MySQL版本是5.7版本。存储引擎是默认的InnoDB

2.2 问题定位

涉及到的表如下:

图片

问题sql如下:

select `bonus_id`, `bonus_code`, `over_at`, `status`, `bonus_num`, `product_id` from `zz_test_table` 
where `user_id` = '16723149' and `over_at` > '2020-11-25 20:45:41' and `status` = 0 
and `bonus_id` in ('382364983', '486697270', '486834963') order by `over_at` asc limit 1;

该sql就涉及一张表zz_test_table(真实表名已被隐藏),表里面有两个索引,一个是over_at字段对应的idx_over_at索引,另一个就是bonus_id字段对应的主键索引。

可以看到,sql其实并不复杂,但是执行结果竟然耗费3秒以上,对于一个面向app用户的接口,3秒以上的响应简直无法接受,如果对业务影响严重点的话,甚至于都需要写事故报告了。

果断祭出explain大法 先来看看原始的查询情况,如下图:

图片

可以看到mysql并没有命中主键索引,而是命中的idx_over_at索引,预估行数为41314647行,这里大家就不要纠结了,为什么这么大的表,历史原因了,后面已经优化掉了。

MySQL官方文档中有描述,我们可以直接强制指定优化器使用我们指定的索引。

图片

强制指定使用主键索引试试

图片

发现使用强制索引之后,sql执行0.103秒就返回了。

问题定位到这里,好像已经比较清楚了,就是MySQL优化器没有正确选择索引导致的呗。

MySQL:我可不背这个锅,你们自己好好反省下。

MySQL说的有道理,为啥好端端的线上会出现3秒的慢查询呢,这个情况之前为什么没有呢,我们先不管人家MySQL优化器的问题,先来分析下,为什么走了idx_over_at索引之后,3秒都没返回数据呢?

那么idx_over_at索引本身是不是有问题呢?,果然,经过排查,是因为有个小兄弟上线的代码有bug,over_at字段被大量写成同一个值,导致我们原本比较均匀的over_at字段存在了大量重复值,索引检索行数指数上升,已经基本类似全表扫描。

还了MySQL清白之后,我们继续来定位下,为什么优化器不使用更高效率的主键索引呢?在这个过程中,我们又发现一些奇怪的现象。

2.3 问题延伸

奇怪现象一:

图片

惊奇的事情发生了,limit 由1 变更为3之后,走了主键索引。

奇怪现象二:

图片

惊奇的事情又发生了,order by 把主键ID加上之后,也走了主键索引。

奇怪现象三:

图片

惊奇的事情继续发生,套了一层子查询,也走了主键索引。

2.4 问题分析

MySQL:是不是很懵逼,如果碰到此类情况,请问阁下应该如何应对?

得,超出理解范畴了,没办法去翻文档吧。MySql5.7官方文档

图片

相对来说,官方的文档关于优化器的说明较为分散,想要快速上手的小伙伴,可以考虑观看阿里云藏经阁出版的深入MySQL实战一书。

附书中关于mysql执行的过程图

图片

再来看书中关于优化器的执行过程图

图片

从以上资料中,我们得出了一些结论,基于这些结论,最后我们可以思考一些解决办法:

这里我考虑使用优化器的trace工具来详细分析下limit 1 和 limit 3为什么走了不同索引。由于trace会影响性能,我们把部分数据还原到本地进行测试,两次执行sql分别如下:

trace分析LIMIT 3

set optimizer_trace="enabled=on";
select `bonus_id`, `bonus_code`, `over_at`, `status`, `bonus_num`, `product_id` from `zz_test_table` where `user_id` = '16723149' and `over_at` > '2020-11-25 20:45:41' and `status` = 0 and `bonus_id` in ('382364983', '486697270', '486834963') order by `over_at` asc limit 3
select * FROM information_schema.optimizer_trace;
set optimizer_trace="enabled=off";

LIMIT 3 分析结果

图片

具体参数解析如下:

trace分析LIMIT 1

set optimizer_trace="enabled=on";
select `bonus_id`, `bonus_code`, `over_at`, `status`, `bonus_num`, `product_id` from `zz_test_table` where `user_id` = '16723149' and `over_at` > '2020-11-25 20:45:41' and `status` = 0 and `bonus_id` in ('382364983', '486697270', '486834963') order by `over_at` asc limit 1
select * FROM information_schema.optimizer_trace;
set optimizer_trace="enabled=off";

LIMIT 1 分析结果

图片

具体参数解析如下:

通过这段日志,我们可以知道查询优化器为了优化查询操作(特别是对 LIMIT 1的优化)做出了一系列的决策和调整,当limit 1的时候,查询优化器认为不使用主键索引的成本会更小。因为这在优化器的成本分析中是更优更快的查询方式。老实说,这里感觉MySQL有点自作聪明了。

3 解决思路

当我们认为SQL的执行计划不合理时,可以使用explain 结合 trace工具去监听整个索引的使用、以及优化器进行优化的一些过程信息,如有必要,可以通过适当的手段去干预优化器。

4 总结

这篇文章是基于工作实际中碰到的问题,把问题产生的原因和解决思路总结了下。文中针对提到的一些索引选择差异情况我们结合了解到的优化器执行策略,使用trace工具进行了验证。优化器有一套非常复杂的算法策略,本人对于MySQL的理解深度有限,这里就不详细分析了,还需要继续学习。

另外了解到MySQL 8.0优化器对查询执行计划的选择做了进一步的改进,理想状态下,会基于估算成本选择最有效的执行计划。感兴趣的小伙伴可以去试试。

来源:转转技术内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯