mybatisplus saveBatch版本问题导致CPU打满生产问题定位

一、生产现象

1、16:57 运维告知Push微服务有一台因为CPU被打满,自动重启,询问原因。

2、17:00 查看异常节点CPU轨迹,16:30开始CPU出现异常飙升

3、17:10 结合生产日志错误,以及定时任务运行情况,得出结论:

产品在16:30新建了一个60w客群的AB分流的任务,

在16:30任务启动,首先从kafka消费拉取大数据客群存储,

kafak消费数据时,将数据批量插入数据库表kafka_data,批量插入异常,同一批数据会再次进行分流,导致死循环,CPU飙升。

Error getting generated key or setting result to parameter object. Cause: org.apache.ibatis.executor.ExecutorException: Too many keys are generated. There are only 4 target objects. You either specified a wrong 'keyProperty' or encountered a driver bug like #1523.

二、紧急修复

日志报错是at com.baomidou.mybatisplus.extension.service.IService.saveBatch(IService.java:58) ~[mybatis-plus-extension-3.2.0.jar!/:3.2.0]

MyBatisplus的saveBatch方法导致,代码紧急修复,弃用saveBatch,手写xml的foreach循环插入。

<insert id="insertABTaskSyncKafkaData" parameterType="java.util.List">

        INSERT INTO t_ab_test_sync_kafka_data

                (task_code, user_id, open_id, mobile)

                VALUES

        <foreach collection="items" item="item" index="index" separator=",">

                (#{item.taskCode, jdbcType=VARCHAR}, #{item.userId, jdbcType=VARCHAR},                 #{item.openId, jdbcType=VARCHAR}, #{item.mobile, jdbcType=VARCHAR})

        </foreach>

</insert>​​​​​​​

三、事后分析

1、本地模拟:

①采用两个线程T1,T2,两个线程分别往线程安全队列abTaskList中写数据,数据在t2线程中存储。(模拟生产kafka消费,一个节点监听主题,不同线程往队列中丢数,并且根据队列大小阈值将队列中一批次数据落库)。

②存数据时,设置DTO对象个别数据为null。(模拟生产客群可别属性为null,例如openId,mobile等)。

本地复现了和生产一样的报错,追踪插入库的List,发现一个批次里面,插成功的对象id被赋值了,没插整个的id为null。

mybatisPlus向上抛出插入的list 和 插成功的list的 id数对不上,

Error flushing statements. Cause: org.apache.ibatis.executor.ExecutorException: Error getting generated key or setting result to parameter object. Cause: org.apache.ibatis.executor.ExecutorException: Too many keys are generated.

There are only 27 target objects. You either specified a wrong 'keyProperty' or encountered a driver bug like #1523.

2、MyBatisplus的saveBatch 技术分析:

①底层xml采用了 useGeneratedKeys="true", 和 ON duplicate KEY UPDATE的底层实现,将DTO存储时,会将id主键重新赋值给DTO对象,如果一个批次里面,有部分主键没有被赋值,则会抛出上面的异常。

②saveBatch方法实际还是一条一条的insert,只是达到阈值1000时,刷一次盘

③跟版本高低有关系,目前Mybatis是3.5.2

3.2.0版本会直接break掉,不会抛异常报错

模拟了几种saveBatch的场景,得出以下结论:

目前报错只存在如下情况同时满足:

1、存在2个以上线程往同一队列丢数

2、对象中有属性为null

3、必须有一个线程既写,又存,然后存的时候会抛异常;如果一个线程里面只有存的动作,也不会出现。

如果用使用MybatisPlus的saveBatch时,如果插入的集合存在多线程共享,同时对象中存在null的属性,最好不要用saveBatch方法,自己手写xml操作批量插入,官方建议一个批次 50~100时插入效率最高。

  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MybatisPlus中的saveBatch方法是用于批量保存数据的方法。在使用saveBatch方法进行批量保存时,需要在数据库连接串中添加&rewriteBatchedStatements=true,并且确保MySQL驱动版本在5.0.18以上。\[1\] 在MybatisPlus中,有两种方法可以实现批量保存数据。第一种方法是使用MybatisPlus自带的Iservice接口,该接口中提供了saveBatch方法用于批量插入数据。这种方法适用于简单的批量插入操作。\[2\] 第二种方法是使用MybatisPlus自定义新增方法。在这种方法中,需要进行一些配置工作,并且编写自定义的批量插入代码。这种方法适用于复杂的批量插入操作。\[2\] 在Iservice接口中,saveBatch方法的定义如下: ``` boolean saveBatch(Collection<T> entityList, int batchSize); ``` 该方法接收一个实体对象集合和插入批次数量作为参数,用于批量插入数据。\[3\] 总结起来,MybatisPlus中的saveBatch方法是用于批量保存数据的方法。可以通过Iservice接口或自定义新增方法来实现批量插入操作。 #### 引用[.reference_title] - *1* [MyBatis-plus 批量新增方法性能测试及优化学习](https://blog.csdn.net/weixin_41645817/article/details/115819312)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [MyBatis-Plus批量保存](https://blog.csdn.net/m0_48847558/article/details/119171236)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值