年底的线上故障,踩坑了最常见的一种索引失效场景

快过年,我的线上发布出现故障

"五哥,你在上线吗?",旁边有一个声音传来。

"啊,怎么了?"。真是要命,在上线发布时候,我最讨厌别人叫我的名字 。我慌忙站起来,看向身后,原来是 建哥在问我。我慌忙的问,怎么回事。

"DBA 刚才在群里说,Task数据库 cpu 负载增加!有大量慢查询",建哥来我身边,跟我说。

慢慢的,我身边聚集着越来越多的人

"你在上线Task服务吗?改动什么内容了,看看要不要立即回滚?"旁边传来声音。此时,我的心开始怦怦乱跳,手心发痒,紧张不已。

我检查着线上机器的日志,试图证明报警的原因不是出在我这里。

我对着电脑,微微颤抖地回答大家:"我只是升级了基础架构的Jar包,其他内容没有改动啊。"此时我已分不清是谁在跟我说话,只能对着电脑作答......

这时DBA在群里发送了一条SQL,他说这条SQL导致了大量的慢查询。

我突然记起来了,我转过头问林哥:"林哥,你上线了什么内容?"这次林哥有代码的变更,跟我一起上线。我觉得可能是他那边有问题。

果然,林哥看着代码发呆。他嘟囔道:"我添加了索引啊,怎么会有慢查询呢?"原来慢查询的SQL是林哥刚刚添加的,这一刻我心里的石头放下了,问题并不在我,我轻松了许多。

"那我先回滚吧",幸好我们刚发布了一半,现在回滚还来得及,我尝试回滚机器。此刻我的紧张情绪稍稍平静下来,手也不再发抖。

既然不是我的问题,我可以以吃瓜的心态,暗中观察事态的发展。我心想:真是吓死我了,幸好不是我的错。

然而我也有一些小抱怨:为什么非要和我一起搭车上线,出了事故,还得把我拖进来。

故障发生前的半小时

2年前除夕前的一周,我正准备着过年前的最后一次线上发布,这时候我刚入职两个月,自然而然会被分配一些简单的小活。这次上线的内容是将基础架构的Jar包升级到新版本。一般情况下,这种配套升级工作不会出问题,只需要按部就班上线就行。

"五哥,你是要上线 Task服务吗?",工位旁的林哥问我,当时我正做着上线前的准备工作。

"对啊,马上要发布,怎么了?",我转身回复他。

"我这有一个代码变更,跟你搭车一起上线吧,改动内容不太多。已经测试验证过了",林哥说着,把代码变更内容发给我,简单和我说了下代码变更的内容。我看着改动内容确实不太多,新增了一个SQL查询,于是便答应下来。我重新打包,准备发布上线。

半小时以后,便出现了文章开头的情景。新增加的SQL 导致大量慢查询,数据库险些被打挂。

为什么加了索引,还会出现慢查询呢?

"加了索引,为什么还有慢查询?",这是大家共同的疑问。

事后分析故障的原因,通过 mysql explain 命令,查看该SQL 确实没有命中索引,从而导致慢查询。

这个SQL 大概长这个样子!我去掉了业务相关的部分。

select * from order_discount_detail where orderId = 1123;

order_discount_detailorderId 这一列上确实加了索引,不应该出现慢查询,乍一看,没有什么问题。我本能的想到了索引失效的几种场景。难道是类型不匹配,导致索引失效?

果不其然, orderId 在数据库中的类型 是 varchar 类型,而传参是按照 long 类型传的。

复习一下: 类型转换导致索引失效

类型转换导致索引失效,是很容易犯的错误

因为在某些特殊场景下要对接外部订单,存在订单Id为字符串的情况,所以 orderId被设计成 varchar 字符串类型。然而出问题的场景比较明确,订单id 就是long类型,不可能是字符串类型。

所以林哥,他在使用Mybatis 时,直接使用 long 类型的 orderId字段传参,并且没有意识到两者数据类型不对。

因为测试环境数据量比较小,即使没有命中索引,也不会有很严重的慢查询,并且测试环境请求量比较低,该慢查询SQL 执行次数较少,所以对数据库压力不大,测试阶段一直没有发现性能问题。

直到代码发布到线上环境------------数据量和访问量都非常高的环境,差点把数据库打挂。

mybatis 能避免 "类型转换导致索引失效" 的问题吗?

mybatis能自动识别数据库和Java类型不一致的情况吗?如果发现java类型和数据库类型不一致,自动把java 类型转换为数据库类型,就能避免索引失效的情况!

答案是不能。我没找到 mybatis 有这个能力。

mybatis 使用 #{} 占位符,会自动根据 参数的 Java 类型填充到 SQL中,同时可以避免SQL注入问题。

例如刚才的SQL 在 mybatis中这样写。

select * from order_discount_detail where orderId = #{orderId};

orderId 是 String 类型,SQL就变为

select * from order_discount_detail where orderId = '1123';

mybatis 完全根据 传参的java类型,构建SQL,所以不要认为 mybatis帮你处理好java和数据库的类型差异问题,你需要自己关注这个问题!

再次提醒,"类型转换导致索引失效"的问题,非常容易踩坑。并且很难在测试环境发现性能问题,等到线上再发现问题就晚了,大家一定要小心!小心!

险些背锅

可能有朋友疑问,为什么发布一半时出现慢查询,单机发布阶段不能发现这个问题吗?

之所以没发现这个问题,是因为 新增SQL在 Kafka消费逻辑中,由于单机发布机器启动时没有争抢到 kafka 分片,所以没有走到新代码逻辑。

此外也没有遵循降级上线的代码规范,如果上线默认是降级状态,上线过程中就不会有问题。放量阶段可以通过降级开关快速止损,避免回滚机器过程缓慢而导致的长时间故障。

不是我的问题,为什么我也背了锅

因为我在发布阶段没有遵循规范,按照规定的流程应该在单机发布完成后进行引流压测。引流压测是指修改机器的Rpc权重,将Rpc请求集中到新发布的单机上,这样就能提前发现线上问题。

然而由于我偷懒,跳过了单机引流压测。由于发布的第一台机器没有抢占到Kafka分片,因此无法执行新代码逻辑。即使进行了单机引流压测,也无法提前发现故障。虽然如此,但我确实没有遵循发布规范,错在我。

如果上线时没有出现故障,这种不规范的上线流程可能不会受到责备。但如果出现问题,那只能怪我倒霉。在复盘过程中,我的领导抓住了这件事,给予了重点批评。作为刚入职的新人,被指责确实让我感到不舒服。

快要过年了,就因为搭车上线,自己也要承担别人犯错的后果,让我很难受。但是自己确实也有错,当时我的心情复杂而沉重。

两年前的事了,说出来让大家吃个瓜,乐呵一下。如果这瓜还行,东东发财的小手点个赞

相关推荐
短剑重铸之日4 分钟前
《SpringBoot4.0初识》第一篇:前瞻与思想
java·开发语言·后端·spring·springboot4.0
it_czz23 分钟前
LangSmith vs LangFlow vs LangGraph Studio 可视化配置方案对比
后端
蓝色王者25 分钟前
springboot 2.6.13 整合flowable6.8.1
java·spring boot·后端
花哥码天下1 小时前
apifox登录后设置token到环境变量
java·后端
hashiqimiya2 小时前
springboot事务触发滚动与不滚蛋
java·spring boot·后端
TeamDev2 小时前
基于 Angular UI 的 C# 桌面应用
前端·后端·angular.js
PPPHUANG3 小时前
一次 CompletableFuture 误用,如何耗尽 IO 线程池并拖垮整个系统
java·后端·代码规范
用户8356290780513 小时前
用Python轻松管理Word页脚:批量处理与多节文档技巧
后端·python
想用offer打牌3 小时前
一站式了解Spring AI Alibaba的流式输出
java·人工智能·后端
秋说3 小时前
华为 DevKit 25.2.rc1 源码迁移分析使用教程(openEuler + ARM64)
后端