Redis分布式锁进阶之事物分布式锁

本文探讨了Redis分布式锁在事务中的应用及存在的问题,并提出通过事务钩子afterCompletion方法优雅地解决锁释放时机的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

视频地址 https://www.bilibili.com/video/BV1et4y1P73Q


redis分布式锁,大家肯定不陌生,也应该都用过,主要作用是防止并发产生的数据问题,下面是一段redis锁的伪代码


public void fun() {
    try {
        String key = "";
        // 获取锁
        if (redisUtils.get(key) == null) {
            // 上锁
            redisUtils.set(key,"",expireTime);
            
            // 业务逻辑处理
            
        }else {
            // 未获得锁
            throw new RuntimeException("未获得锁");    
        }
    }finally {
        // 释放锁
        redisUtils.remove(key);
    }
}

在实际使用分布式锁还存在一个问题,我们很多方法是有事物的,如果在上述方法加了一个事物,事物是要在方法执行完之后才提交的,也就是会先释放锁,后提交事务,如果在释放锁和提交事务之间有一个线程访问了,这时候它获取到了锁,但是去查询数据库的时候还无法读取前一个事物没有提交的数据,这时候就会造成数据错乱。


我们可以使用事物钩子,可以确保在事物提交之后再去执行释放锁

TransactionSynchronizationManager.registerSynchronization(
    new TransactionSynchronizationAdapter() {
    @Override
    public void afterCommit() {
        redisUtils.remove(key);
    }
});

改造后面的伪代码如下

@Transactional(rollbackFor = Exception.class)
public void fun() {
    try {
        String key = "";
        // 获取锁
        if (redisUtils.get(key) == null) {
            // 上锁
            redisUtils.set(key,"",expireTime);

            // 业务逻辑处理

        }else {
            // 未获得锁
            throw new RuntimeException("未获得锁");
        }
    }finally {
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 释放锁
                redisUtils.remove(key);
            }
        });
    }
}

上面的代码看似天衣无缝,但在实际使用场景中又出问题了,在执行业务代码的时候出现了各种异常(这是很正常的事情),如果出现了异常,事物是会回滚的,这时候就不会走下面的释放锁逻辑,只有等锁过期,这将是致命的。


做一点修改就可以完美的解决这个问题

@Transactional(rollbackFor = Exception.class)
public void fun() {
    try {
        String key = "";
        // 获取锁
        if (redisUtils.get(key) == null) {
            // 上锁
            redisUtils.set(key,"",expireTime);

            // 业务逻辑处理

        }else {
            // 未获得锁
            throw new RuntimeException("未获得锁");
        }
    }catch (Exception e){
        // 释放锁,抛出异常
        redisUtils.remove(key);
        throw e;
    }finally {
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 释放锁
                redisUtils.remove(key);
            }
        });
    }
}

PS:最开始出现这个问题的时候,我总在想是不是事物的传播行为导致事物失效了,或者是开启了新的事物,尝试了很多次,最后发现是事物回滚了。


事物钩子里全部的方法

// 挂起时出发
@Override
public void suspend() {
}

// 挂起事物抛出异常的时候会触发
@Override
public void resume() {
}

@Override
public void flush() {
}

// 在事物提交之前触发
@Override
public void beforeCommit(boolean readOnly) {
}

// 在事物完成之前触发(回滚/提交)
@Override
public void beforeCompletion() {
}

// 在事物提交之后触发
@Override
public void afterCommit() {
}

// 在事物完成之后触发 (回滚/提交 可以用status来判断)
@Override
public void afterCompletion(int status) {
}

上面我们的问题,可以有更优雅的解决办法,把 afterCommit 方法换成 afterCompletion 即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值