发生场景:车间产出记录进行出库
国庆节前下班的时候,生产部门生产完进行入库,这个时候正常跑我们的业务XXXX——直接到根据单件档案的单件号更新单件档案上的最终用户信息时候出现了问题,跑着跑着就超时,这个时候就进行问题排查
1、首先怀疑是大量数据操作时候怀疑是不是有锁表情况发生,但是跟踪了下数据库并没有发生锁表的现象
2、排除锁表情况,然后开始跟踪整个业务的sql,排查哪条sql出现了超时
3、最终排查到是一条更新语句,回写最终用户时候那条update 超时了,在执行到第51s时候出现等待超时nnodb_lock_wait_timeout
原因分析
先来看下这条更新的sql,其中使用了case when
<update id="updateSn">update a1<trim prefix="set" suffixOverrides=","><trim prefix="C01 =case" suffix="end,"><foreach collection="list" item="i" index="index"><if test="i.sn != null">when a1.sn = #{i.sn} then #{i.finalUser,jdbcType=VARCHAR}</if></foreach></trim><trim prefix="C02 =case" suffix="end,"><foreach collection="list" item="i" index="index"><if test="i.sn != null">when a1.sn = #{i.sn} then #{i.cpType,jdbcType=VARCHAR}</if></foreach></trim></trim>where a1.sn in<foreach collection="list" item="i" index="index" open="(" separator="," close=")">#{i.sn,jdbcType=VARCHAR}</foreach></update>
原因是list里面有上万条数据,就会造成如下情况
update a1
set C01 = case when a1.sn = 'SN001' then '张三' when a1.sn = 'SN002' then '李四' ......................end,C02 = case when a1.sn = 'SN001' then '类型A' when a1.sn = 'SN002' then '类型B' ......................end
where a1.sn in ('SN001', 'SN002',.........)
排查说明
从而造成的直接原因就是这条更新语句太过庞大,执行50s后依然没有能结束,那么就会出现超时的异常,原因是UPDATE 语句在执行时会对涉及的行 / 表加锁(InnoDB 引擎默认行级锁),如果其他事务已持有相关锁(如未提交的更新、删除操作),当前 UPDATE 会进入锁等待状态。当等待时间超过 innodb_lock_wait_timeout(默认 50 秒)时,就会触发超时(Lock wait timeout exceeded 错误)
select操作一般不会有这个情况,因为默认情况下(非 FOR UPDATE 或 LOCK IN SHARE MODE),InnoDB 使用快照读(借助 MVCC 机制),不会加锁,也不会等待其他事务释放锁,因此可以长时间运行而不触发锁等待超时
这个时候修改代码来不及,因为不能让系统重启,所以让业务部门减少数据量,分批操作。。。。方法可能比较笨,但是能保证业务顺利入库,进行后续操作
后续优化
数据量太大,分批次执行,每次1000条更新一次
这样写的有点