最近工作过程中遇到了死锁还有锁等待,想整理出来分享给大家
死锁场景描述
功能1.项目同步用户的定时任务(每五分钟执行一次),执行逻辑如下,项目中通过http请求将ams系统变更的用户查询出来,for遍历处理即执行更新或者插入操作. 功能2.项目中业务接口,根据用户ID批量修改用户业务系数,执行逻辑如下,
<update id="updateDividedRatio">
update user
set DIVIDED_RATIO = #{dividedRatio}
where 1=1
<if test="ids !=null and ids.size()>0">
and id in
<foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
where ID = #{id}
</update>
线上运营操作功能2之后,界面报错,查看日志发现dead Lock,发现死锁了...
原因分析
功能1与功能2处理重叠了,意思是功能1定时任务处理过程中,运营人员操作了功能2,造成了死锁...什么...这... 分析:功能1处理完userId=3的用户,接下来要处理userId=5的用户同步信息 此时功能2处理完userId=5的用户,接下来要处理userId=3的用户业务系数 互相等...然后就死锁了...
解决办法
将功能2进行拆分事务,逻辑里面for遍历用户,每单个用户执行更新,避免update USER set xxx where ID in(xxx,xxx,xxx,...),上述做法其实执行了事务拆分,将大事务拆分成一个一个小事务处理.
<update id="updateDividedRatio">
update cs_user
set DIVIDED_RATIO = #{dividedRatio}
where ID = #{id}
</updat>
锁等待场景描述
接口1 http请求调用接口2,接口1使用@Transactional注解进行事务管理,接口2记不清楚有没有使用事务了.
接口1中更新userId=3用户之后,http调用接口2也是要执行更新userId=3用户,接口1在等待接口2请求返回结果继续接下来的处理,那问题来了,,,接口1处理userId=3用户事务未提交,接口2再去处理userId=3用户时需要等待数据库行级锁提交(看备注,根据userId更新使用的是主键索引,即行级锁)
接口1等待接口2返回后继续逻辑处理,接口2等待接口1的数据库行锁,等到天荒地老了...凉凉了
备注:
1)在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁
2)由于MYSQL行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如图是使用相同的索引键,是会出现锁冲突的,应用设计的时候要注意这一点
3)当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同行,另外,不论是使用主键索引,唯一索引或者普通索引,InnoDB都会使用行锁来对数据加锁
日志截图
原因分析
锁等待场景描述中已详述
解决办法
解决办法1:将接口1的事务注解@Transactional去掉,即随操作数据库随提交,看业务接口这样处理是否符合需求 解决办法2:重复处理了,更新用户操作看下是放在接口1中处理还是接口2中处理.