多线程环境下生成重复序号问题

结论:

设置事务隔离级别和传播方式不对,导致不同线程从数据库获取的序列号相同,导致序列号重复。

解决办法,调整事务隔离级别以及传播级别。

代码环境:

springboot:2.7.6

java:jdk8

mysql:8.0

伪代码:

public class BusinessProcess{
    @Resource
    private AccountRuleService accountRuleService;
    
    @Transaction()
    public void openAccount(User user){
        check(user);
        String account = accountRuleService.generate(user.getBusinessType());
        user.setAccount(account);
        saveUser(user);
    }
}

public class AccountRuleService {
    
    @Resource
    private RedissionClient redissionClient;
    @Resource
    private AccountRuleMapper accountRuleMapper;
    
    private final String LOCK_KEY = "lock_key";

    public String generate(String businessType){
      RLock lock = redissionClient.getLock(LOCK_KEY)
      try{
        String serialNo = accountRuleMapper.selectSerialNo(businessType);
        serialNo = incrementSerialNo(serialNo);
        accountRuleMapper.update(businessType,serialNo);
        return serialNo;
      }
      finally{
          lock.unlock(LOCK_KEY);
      }  
    }
}



业务流程:

后端接收http请求,调用BusinessProcess,由AccountRuleServic生成序号。最后回写数据库。

存在问题:

1. redissionClient 只创建了lock但是没有lock()

这会导致多个线程同时获取数据库表序列号,拿到相同的序列号

2. AccountRuleService#generate()方法没有单独设置事务

这里没有单独设置事务会存在一个严重的问题

当线程1生成序号后,但未还没提交事务时。线程2读取序号时,还是获取旧的序号。发生读取重复序号。

解决办法

1. RedissionClient获取lock后要加锁

2. 通过调整generate()方法的事务隔离级别以及传播方式

propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED

重点在于READ_UNCOMMITTED

保证了当线程1执行update语句后能被其他线程观测到

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值