• 记一次for update“同一事务”中update无法获取数据锁的解决


    背景:银行多个异步通知先后到达,需要依次更新同一条数据A(wherte acountId=aaa)(acountId是唯一索引)的不同状态,每一次更新需要在上一次更新的基础上进行。

      及数据A(wherte acountId=aaa)原本状态status=0、openstatus=0

      ——》在收到通知1后,在方法methodA中,将状态更新为status=1、openstaus=0,注更新前需要查询到数据A(wherte acountId=aaa)

      ——》在收到通知2后,在方法methodB中,将状态更新为status=1、openstaus=1或者status=1、openstaus=2,注更新前需要查询到数据A(wherte acountId=aaa)

    注:1、数据A的大概字段:sysNo(主键)、accountId(唯一键)、status、openStatus、createTime、......

      2、在进行某一项交易后,通知1和通知2会先后到来,但是先后的时间差不能保证,那么存在两种情况:情况A,通知2来时执行方法methodB时可能通知1的处理方法methodA已经执行完毕,情况B,也可能methodA方法还在执行中。

      为了防止情况B在methodB开始执行的时候,methodA还未执行完毕导致的数据更新不是在最新数据上进行的更新(专业术语:更新丢失或者更新覆盖),及methodB是直接在原始数据上进行的更新,而不是在methodA更新后的数据上进行更新的。为了防止更新丢失,可以有多种解决办法:悲观锁(for update)方式、乐观锁(版本号、时间戳)方式、通过分布式锁(redis)等。

    问题:由于公司项目并发不高,所以直接使用悲观锁来简单处理的,但是在处理过程中发现一个奇怪的现象(注该现象是在开发和测试环境中出现),如下图:

    描述:在同一个事务中,第一步通过for update获取到的排他锁,但是在进行更新update的时候发现该笔数据的排它锁却无法获取,是不是感觉很奇怪?

    于是下面就开始找问题,为什么update获取不到上面的同一笔数据的锁?

    一、定位数据更新update语句,是不是更新到了索引?检查了update语句,在update语句中并没有更新到索引字段。

    二、在数据库中直接执行是否报错?数据库中直接执行没有问题,说明代码中前后执行不是在同一个事务中(定位问题)。

    三、数据库事务是不是统一管理的?数据库索引会不会存在一部分spring管理,一部分mybatis管理?确认了数据库事务统一由Spring管理,所以不是该原因造成的。

    四、是否使用主从数据库导致数据来源不一致?

      描述:公司生产上使用主从数据库,本地开发和测试环境都是单一数据库,没有主从,但是通过ShardingJDBC配置了主从设置

    问题原因就在这,虽然测试环境实际上只有一个数据库,但是ShardingJDBC配置了主从,那么即使只有一个数据库,也会产生两条连接,一条用于主、一条用于从,所以在select for update语句的时候默认使用从链接的事务中获取数据,并且将数据加锁,而表面上看代码是同一事务的update时,实际上是使用主链接的事务进行更新数据,所以select 和 update不在同一个事务中,故代码层面看是同一事务的先后两个操作,update时实际获取不到之前的select的锁,所以发生等待直至超时报错。

    解决办法:强制统一使用主链接读取和修改数据,代码如下:

    补充:

    1、悲观锁导致的死锁

    在使用悲观锁的过程中,注意加锁顺序,如果methodA方法是更新数据1和数据2,而methodB是更新数据2和数据1,如果两个线程同时进入到methodA和methodB,分别对数据1和数据2上锁,然后又分别等待数据2和数据1释放锁后自己获取,那么就会造成死锁。

      避免死锁的方法:

      A:统一加锁顺序,例如按照id自然序来进行加锁操作,这样事务之间的加锁操作就不会存在死锁。

      B:不使用悲观锁(乐观锁、分布式锁、Redis锁)或者在使用悲观锁钱在加一个全局锁,如在redis存储一个lock标记,当事务获取到这个lock标记时,才允许进行更新操作,否则等待锁。

    2、ShardingJDBC

      当当网开发的主要用于处理数据分片和读写分离的框架。

  • 相关阅读:
    quartz 定时调度持久化数据库配置文件
    springboot项目下mvnw文件的作用
    mysql安装版卸载,解压版安装
    idea提示,格式化代码,清除不使用的包快捷键,maven自动导jar包
    JavaScript中call,apply,bind方法
    彻底理解js中this的指向
    Gradle系列之从init.gradle说起
    响应式网页设计简单入门
    开启MySQL远程访问权限 允许远程连接
    https原理
  • 原文地址:https://www.cnblogs.com/yanzige/p/13969853.html
Copyright © 2020-2023  润新知