Redis分布式锁业务没执行完但锁超时了怎么办? --Redisson

本文介绍了在使用Redis作为分布式锁时可能遇到的问题,即业务未完成但锁已超时。文章推荐使用Redisson库来实现安全的分布式锁,Redisson的可重入锁提供了看门狗功能,能够在服务宕机时自动延长锁的有效期,确保锁的释放。文中还展示了Redisson的配置和使用示例,以及看门狗超时时间的设置和测试结果,强调了避免指定锁过期时间以启用看门狗的重要性。

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

文章首发于有间博客,欢迎大伙光临Redis分布式锁业务没执行完但锁超时了怎么办? --Redisson

问题前言

这个问题的思考源自JD的面试题,因为项目中有使用分布式锁,切切实实的被问到了这个问题,在面试中也没有答得很好,现在有空了来对问题进行一个解决思考和整理。

在项目中如果使用redis原生的分布式锁,setnx设置并使用expire定义超时时间,就有可能出现锁的时间到了但业务没有执行完毕的情况。那如果不设置超时时间,万一服务宕机没来得及解锁呢,那这个分布式锁就再也不能访问了。

Redisson

由此,我们可以使用redis设置分布式锁的正确姿势 –Redisson,通过redisson我们就能安全的下车!

深入了解可以看Redisson 文档: redisson

我们可以根据redisson的可重入锁,来设置分布式锁,使用的方式和java中的ReentrantLock基本一致。并且redisson内部提供了一个监控锁的看门狗,在redisson实例被关闭前,会不断的延长锁的有效期,如果redisson实例被关闭了,那么锁时间到后自动关闭。
在这里插入图片描述

问题解答

so,上述面试的问题也就可以有了答案了,我们可以使用redisson客户端进行分布式锁的设置,使用tryLock方法对分布式锁的设置进行判断,执行不同的逻辑,如果业务逻辑执行的过程中服务宕机,那么会由redisson提供的看门狗监控锁进行兜底,在实例结束后的一段时间内对锁进行清除。看门狗设置过期的时间默认是30s,每30 / 3 s进行一次判断,我们也可以对看门狗时间进行一个手动的设置。

注意点

在用redisson设置分布式锁的时候,如果想让看门狗监控锁生效,那么则不能指定锁的过期时间,不然无效,来看看官方文档是怎么说的。
在这里插入图片描述

实例测试

在使用redisson之前需要先设置pom依赖和配置redisson的配置文件

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.1</version>
        </dependency>

redisson配置

@Configuration
public class redissonConfig {

    @Bean
    public RedissonClient configRedisson(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://172.0.0.1:6379")
        .setPassword("777777");
        config.setCodec(new StringCodec());
        //设置看门狗的时间,不配置的话默认30000 
        config.setLockWatchdogTimeout(12000);
        RedissonClient redisson =  Redisson.create(config);
        return redisson;
    }
}

完毕后就可以在业务逻辑中引入调用。一个简单的小例子

    @Autowired
    RedissonClient redisson;

    @GetMapping(value = "/getWatchDogLock")
    public String getWatchDog() throws InterruptedException {
        RLock watchDogLock = redisson.getLock("watchDogLock");
        boolean judge;
        //进行3s的尝试时间,如果失败则返回false, 还可以设置锁过期时间,如果设置会导致看门狗无效
        judge = watchDogLock.tryLock(3,  TimeUnit.SECONDS);
        //输出是否能够获取到锁
        System.out.println(Thread.currentThread().getName() + "的锁获取情况:" + judge);
        //如果获取到锁
        if (judge){
            try {
                System.out.println(Thread.currentThread().getName() + " 已经成功获取到的分布式锁" + System.currentTimeMillis());
                //执行主要业务逻辑
                Thread.sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
            	//最后进行解锁操作,如果服务宕机,则到不了这一步,会等待看门狗监控锁结束锁清除
                System.out.println(Thread.currentThread().getName() + " 进行解锁操作" + System.currentTimeMillis());
                watchDogLock.unlock();
            }
        }else {
        	//如果没有获取到分布式锁,执行业务逻辑
            System.out.println(Thread.currentThread().getName() + " 没有成功获取到分布式锁,锁已经被占用" + System.currentTimeMillis());
        }
        //最后输出
        if (judge){
            return "成功获取到分布式锁";
        }
        return "没有获取到分布式锁";
    }
测试结果

在上述配置文件中设置了看门狗监控锁的时间为12s,在锁获取后我对服务进行了中断,来看看该锁的超时时间变化。

中断前:可以看到每当时间减少了1/3 就会重新恢复到12,也就是降低到8就会判断redisson实例是否存活,恢复时间。
在这里插入图片描述
中断后:中断后等待看门狗监控锁配置的时间结束后,分布式锁消失。
在这里插入图片描述

总结

总结一下,在我们使用redisson客户端设置分布式锁的时候,如果担心有业务逻辑没有执行完毕而锁过期的情况,会使用看门狗监控锁进行兜底,防止服务的宕机。看门狗的时间可以在配置中使用LockWatchdogTimeout 进行设置,视业务情况而定。

如有错误的地方请在评论区指出探讨。

### Redis 分布式锁Redisson 的实现原理 #### 1. **Redis 分布式锁** Redis 分布式锁的核心思想是通过 `SETNX` 和 `EXPIRE` 来实现加操作。在早期版本(Redis 2.6.12之前),由于 `SETNX` 不支持设置过期时间,因此需要分两步成:先调用 `SETNX` 创建键值对表示定状态,再调用 `EXPIRE` 设置过期时间以防止死[^3]。 然而这种两步操作无法保证原子性,可能会因网络延迟或其他异常导致未成功创建却设置了过期时间。为此,Redis 2.6.12引入了新的命令 `SET key value NX PX milliseconds`,其中 `NX` 表示只有当键不存在时才执行设置操作,`PX` 则用于指定毫秒级的过期时间。这种方式能够在一个命令内成加并设定超时,从而有效解决了上述问题。 #### 2. **Redisson 实现分布式锁** Redisson 是基于 Redis 开发的一个 Java 客户端库,它不仅实现了更高级别的抽象接口,还提供了多种类型的分布式对象和服务功能。对于分布式锁而言,Redisson 提供了一种更加健壮可靠的解决方案——`RLock` 接口及其子类实例化方式。 - **加逻辑** Redisson 使用 Lua 脚本来确保整个加过程具有原子性。该脚本会检查目标资源是否已被占用;如果未被占用,则尝试获取并将当前线程 ID 记录下来作为持有者的唯一标识符[^1]。 - **续命机制** 当某个客户端成功获得之后,Redisson 会在后台启动一个定时器任务定期向服务器发送续约请求延长的有效期限,直到显式解为止。此设计可以避免因长时间运行的任务而导致提前失效的情况发生[^2]。 - **自旋重试策略** 如果初次未能取得所需资源,则按照预定义间隔不断重复尝试直至达到最大等待时限或最终放弃争夺控制权。 - **公平性和可靠性保障措施** 在某些特殊情况下(比如网络分区), 可能会出现部分节点认为自己已经拿到了全局唯一的,但实际上其他地方也有竞争者存在的情形下, redisson 还特别考虑到了这一点并通过内部复杂的协调算法尽可能减少冲突概率[^4]. #### 性能对比分析 | 特性 | Redis 原生分布 | Redisson | |-------------------------|------------------------------------------|----------------------------------| | 加效率 | 较高 | 略低 | | 安全性 | 存在网络抖动等问题 | 更安全可靠 | | 功能扩展能力 | 单纯提供基础加解鎖功能 | 支持更多特性如自动续租、可重入等 | | 易用程度 | 需要开发者手动处理很多细节 | API 封装良好易于集成 | 从表中可以看出虽然原生态方法简单高效但在实际应用过程中往往面临诸多挑战;而借助第三方工具包则可以在一定程度上弥补这些不足之处. ```java // 示例代码展示如何利用Redisson进行分布式锁管理 import org.redisson.api.RLock; import org.redisson.api.RedissonClient; public class DistributedLockExample { private final RedissonClient redissonClient; public void acquireAndReleaseLock(String lockName) throws InterruptedException{ RLock lock = redissonClient.getLock(lockName); try { boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS); // 尝试获取最长等待时间为10秒 if(isLocked){ System.out.println(Thread.currentThread().getName()+" acquired the lock."); Thread.sleep(5000L); // Simulate some work }else{ System.err.println("Failed to get lock after waiting..."); } } finally { if(lock.isHeldByCurrentThread()){ lock.unlock(); System.out.println(Thread.currentThread().getName()+ " released the lock."); } } } } ``` ###
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值