【事故总结】Mybatis-Wrapper导致的生产事故

近期遭遇了一次生产环境的严重告警,涉及慢接口和CPU过载。经过排查,发现问题根源在于一段使用MyBatis的查询代码。当传入空列表作为查询条件时,MyBatis会忽略该条件,导致全表扫描,进而引发系统资源耗尽和频繁的Full GC

灾难回顾

【事故总结】Mybatis-Wrapper导致的生产事故 - 程序员古德

前两天晚上,正在收拾包准备下班,电脑刚放进包里,我的手机就开始不停地震动,打开一看,是生产环境告警群发来的信息,一段是慢接口告警,另一段是CPU告警。在这种即将回家的时候看到告警信息,是每一个技术人员最不想发生的事情,但我依然从包里取出电脑,准备处理这一突发情况。

由于最近并没有上线新功能,我的第一反应是怀疑中间件或数据库出了问题,毕竟,经验告诉我,很多时候性能问题都是由这些基础设施引起的,但经过和运维同事的一轮快速排查,各中间件表现正常,数据库中的慢SQL也并未达到触发告警的阈值。

看来问题比我想象的要复杂,我坐在运维同事旁边,让他登录主机上执行top命令,看看究竟怎么回事,从TOP可以看出,CPU占用情况异常地显示在主机CPU一直保持在100%(主机16核),没有任何下降的趋势,这意味着,某个进程或者线程正在疯狂地消耗CPU资源。

我突然想起之前遇到过的类似情况,那次是因为JVM的Full GC过于频繁导致的,有了上次的经验,我就让运维执行jstat -gcutil pid 1000命令查看GC情况,果不其然,FGC(Full GC)次数每几秒就增加1次,这频率高得异常,JVM显然在不停地进行垃圾回收,但为什么会这么频繁呢?

在这种情况下肯定是要先保住业务系统的运行,我的第一反应是尽快减轻服务器的压力,于是,我向领导申请决定重启部分机器,同时留下一台机器进行内存快照(dump)的生成,以便后续分析。10分钟后,内存快照分析完成。分析结果让我颇为震惊。有个叫OrderAddress的类占据了异常大的内存空间,包含对象数高达150多万。这明显不正常,很可能是它导致了频繁的Full GC。

为了找到问题的根源,我使用jstack命令(jstack -l pid)查看了JVM的线程堆栈信息。通过搜索关键词OrderAddress,我发现了很多与之相关的堆栈信息。这些信息就像是破案的关键线索,指引我逐步接近问题的真相。最终,基于这些堆栈信息,我找到了对应的代码位置。

场景回顾

【事故总结】Mybatis-Wrapper导致的生产事故 - 程序员古德

在我们日常的开发工作中,经常会遇到与数据库交互的情况。假设我们有这样一张数据库表jy_order_address,它记录了订单的收货地址信息:

create table `jy_order_address` (`id` int(11) unsigned not null auto_increment comment 'id',`order_id` int(11) not null comment 'jy_order.id',.........
) comment='订单收货地址';

为了满足某一业务需求,我们需要根据一批order_id来查询对应的收货地址,在Java中,使用MyBatis作为ORM框架,我们可能会写出如下的代码:

public List<OrderAddress> selectByOrderIdList(List<Integer> orderIdList) {Wrapper<OrderAddress> wrapper = new EntityWrapper<>();wrapper.in("order_id", orderIdList);List<OrderAddress> orderAddressList = orderAddressMapper.selectList(wrapper);return orderAddressList;
}

这段代码逻辑很清晰,通过MyBatis的Wrapper来组装SQL查询条件,实现在order_id在指定列表中的筛选。

正常情况下,如果orderIdList数据量不大,即使jy_order_address表中有大量数据,上述查询也是非常高效的,对应的SQL如下:

select * from jy_order_address where order_id in(?,?,?,...);

然而,当传入一个空列表orderIdList = []时,系统CPU占用率飙升,经过排查,发现竟然进行了全表扫描!

深入了解MyBatis的WrapperAPI后,我们发现了一个容易被忽视的细节,当查询条件的value为null或者空列表时,MyBatis会忽略该条件,这意味着上述查询代码在实际执行时变成了无条件的全表查询:

select * from jy_order_address;

对于一个包含150多万条数据的表来说,全表扫描无疑是一个巨大的负担,它会消耗大量的内存和CPU资源,进而导致JVM频繁进行Full GC(全局垃圾回收)。

预防措施

【事故总结】Mybatis-Wrapper导致的生产事故 - 程序员古德

严格的参数校验,在使用Wrapper进行查询之前,应对每个参数进行非空校验,这确保了查询的准确性和系统的健壮性,防止因空值或无效值导致的不可预测的行为。

**审慎使用Wrapper,尽管Wrapper提供了方便的查询条件组装,但在某些情况下,它可能导致不期望的查询行为。**为了避免这种情况,推荐直接编写SQL语句,特别是在处理关键查询时。例如,使用order_id in()这样的条件时应当特别小心,因为这可能导致SQL语法错误,一个更安全的方法是添加一个始终为假的条件(如1<>1),这样即使其他条件出现问题,SQL仍然可以正确执行,只是返回0条数据。

限制查询结果,为了防止大量数据一次性加载到内存中导致系统崩溃,建议使用MyBatis拦截器为所有查询统一设置结果条数上限,例如,每次查询最多返回1000条记录,当达到这个限制时,系统可以发出警告,并提示开发者进行分页查询以获取更多数据,这样不仅能提高系统的稳定性,还能提升用户体验,使大数据量的处理更加流畅。

关注我,每天学习互联网编程技术 - 程序员古德

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/307654.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《别让猴子跳回背上》——管理者的时间管理

讲时间管理的书很多&#xff0c;但这本是专门讲给管理者的时间管理。 在职场中&#xff0c;许多管理者都会碰到工作计划执行不下去、组织目标难于实现的问题&#xff0c;搭进了自己所有可以支配的时间&#xff0c;仍旧是焦头烂额&#xff0c;顾此失彼&#xff1b;而下属则因为…

PowerShell Instal 一键部署TeamCity

前言 TeamCity 是一个通用的 CI/CD 软件平台,可实现灵活的工作流程、协作和开发实践。允许在您的 DevOps 流程中成功实现持续集成、持续交付和持续部署。 系统支持 Centos7,8,9/Redhat7,8,9及复刻系列系统支持 Windows 10,11,2012,2016,2019,2022高版本建议使用9系列系统…

LabVIEW各版本安装指南

链接地址如下&#xff1a; https://pan.baidu.com/s/1NF9hY03bApwwpI-WVHGlZg?pwd0531 1.鼠标右击【LabVIEW2023】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 LabVIEW2023】。 2.打开解压后的文件夹&#xff0c;双击打开【Setup】文件…

C语言 volatile关键字

volatile关键字介绍 volatile 是一个关键字&#xff0c;用于修饰变量&#xff0c;表示该变量是易变的&#xff0c;即可能在任何时候被意外地改变。在多线程编程中&#xff0c;当多个线程同时访问同一个变量时&#xff0c;由于线程之间的交互和优化&#xff0c;可能会导致变量的…

【挑战全网最易懂】深度强化学习 --- 零基础指南

深度强化学习介绍、概念 强化学习介绍离散场景&#xff0c;使用行为价值方法连续场景&#xff0c;使用概率分布方法实时反馈连续场景&#xff1a;使用概率分布 行为价值方法 强化学习六要素设计奖励函数设计评论家策略学习与优化 算法路径深度 Q 网络 DQN演员-评论家算法&…

echarts常见的一些大屏示意图及配置项【好看】

双立体柱状图 示意图&#xff1a; 配置&#xff1a; initData() {let sideData [220, 182, 191, 234, 290, 330]let sideData1 [100, 110, 120, 134, 190, 230]let nameList [结算能力数, 结算金额]let yAxisData [(金额/亿元), (能力数/个)]let xData [1, 2, 3, 4, 5…

Anolis安装Jdk保姆级教学

前言 欢迎来到本博客&#xff0c;我们将带领你完成在Anolis操作系统上安装Java Development Kit&#xff08;JDK&#xff09;的详细过程。Anolis操作系统是一款基于Linux的轻量级操作系统&#xff0c;专为容器和云原生应用而设计。在Anolis上安装JDK将为你提供一个稳定、高效的…

1万亿元国债支持水利、应急行业,钡铼智能终端积极助力提升防灾抗洪建设需求

10月24日&#xff0c;十四届全国人大常委会第六次会议审议通过了国务院关于增加发行国债支持灾后恢复重建和提升防灾减灾救灾能力的议案。为贯彻落实中共中央政治局常委会会议精神&#xff0c;以强有力的资金保障有关工作落实&#xff0c;中央财政将在今年四季度增发2023年国债…

证明:切线垂直于半径

证明&#xff1a; 切线垂直于过切点的半径。 下面是网上最简单的证明方法。 证明&#xff1a; 利用反证法。 如下图所示&#xff0c;直线AB和圆O切于点A&#xff0c;假设OA 不垂直于 AB&#xff0c;而 O B ⊥ A B OB \perp AB OB⊥AB&#xff0c;则 ∠ O B A 90 \angle OB…

十、方法调用的底层实现

一、方法调用分析&#xff08;main方法是JVM指令执行的起点&#xff09; 我们写的代码&#xff0c;经过编译、经过类加载的各种阶段&#xff0c;进入了 JVM 的运行时数据区。但作为程序员真正关心是代码的执行&#xff0c;代码的执行其实本质上是方法的执行&#xff0c;站在 JV…

6、LLaVA

简介 LLaVA官网 LLaVA使用Vicuna(LLaMA-2)作为LLM f ϕ ( ⋅ ) f_\phi() fϕ​(⋅)&#xff0c;使用预训练的CLIP图像编码器 ViT-L/14 g ( X v ) g(X_v) g(Xv​)。 输入图像 X v X_v Xv​&#xff0c;首先获取feature Z v g ( X v ) Z_vg(X_v) Zv​g(Xv​)。考虑到最后一…

WPF+Halcon 培训项目实战(8):WPF+Halcon初次开发

前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原因无非是想换个工作。相关的教学视频来源于下方的Up主的提供的教程。这里只做笔记分享&#xff0c;想要源码或者教学视频可以和他联系一下。 相关链接 微软系列技术教程 WPF 年度公益课程 Halcon开发 CSD…