【方案设计】分布式锁相关场景

【方案设计】分布式锁相关场景

1、用了一锁二查三更新,为什么还出现重复数据?

解决接口幂等问题,最常用的方式是:一锁、二判、三更新。

用这个这个方式做幂等防控,就一定不会出现脏数据了么?比如以下代码:

@Transactional(rollbackFor = Exception.class)
public boolean order(Request request) {

	RLock lock = redisson.getLock(request.getIdentifier());
    try {
        //一锁
        lock.lock();

        //二查
        OrderDo order = orderMapper.find(request.getIdentifier());
        if (order!=null) {
            resp.setResultFail(ReturnCodeEnum.USER_EXIST);
            return false;
        }

        //三更新,保存订单数据
        orderMapper.insertOrder(request);
        
    } finally {
        lock.unlock();
    }


    //三更新,保存订单流水
    orderStreamMapper.insertOrder(request);
    //三更新,保存事件流水
    eventMapper.insert(request);
    return true;
}

以上代码,其实是有可能出现脏数据的。

首先,代码中通过@Transactional在order方法上增加了一个事务,事务的范围是整个方法,也就是方法全部执行完,事务才会提交。

也就是说,代码完第28行(return true;)之后,事务才会提交。可是锁的释放,是在第20行(lock.unlock();)就执行了。

那么就会出现一个问题,锁已经释放了,但是事务还没提交。这时候其他的线程在并发请求过来的时候:

一锁。拿锁可以拿到,因为锁被释放了
二查。查询数据也查不到,因为这时候之前的那个事务可能还没提交,未提交的数据,新的事务是看不到的。
三更新。执行更新操作,导致数据重复。

以上,就是因为事务的粒度大于锁的粒度,并且因为事务之间隔离性,导致并发产生了脏数据。

想要解决这个问题,就是减小事务的粒度,或者增大锁的粒度,不要在事务提交前就把锁释放掉。

减小事务粒度,这时候就可以把声明式事务改成编程式事务,自己控制锁的粒度:

@Autowired
private TransactionTemplate transactionTemplate;

public boolean order(Request request) {

	RLock lock = redisson.getLock(request.getIdentifier());
    try {
        //一锁
        lock.lock();
        //二查
        OrderDo order = orderMapper.find(request.getIdentifier());
        if (order!=null) {
            resp.setResultFail(ReturnCodeEnum.USER_EXIST);
            return false;
        }

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                //三更新,保存订单数据
            	orderMapper.insertOrder(request);
                //三更新,保存订单流水
                orderStreamMapper.insertOrder(request);
                //三更新,保存事件流水
                eventMapper.insert(request);  
            }
        });
    
    	return true;
    }finally {
        redisLock.unLock();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值