死锁 or 锁等待

242 阅读3分钟

最近工作过程中遇到了死锁还有锁等待,想整理出来分享给大家

死锁场景描述

功能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都会使用行锁来对数据加锁

日志截图

image.png

原因分析

锁等待场景描述中已详述

解决办法

解决办法1:将接口1的事务注解@Transactional去掉,即随操作数据库随提交,看业务接口这样处理是否符合需求 解决办法2:重复处理了,更新用户操作看下是放在接口1中处理还是接口2中处理.