Redisson分布式锁:看门狗机制与续期原理
文章目录
- 前言
- 一、分布式锁的基本概念
- 1.1 什么是分布式锁
- 1.2 为什么选择Redisson
- 二、Redisson分布式锁的使用
- 2.1 基本使用示例
- 2.2 可重入锁示例
- 2.3 看门狗机制的自动续期
- 三、底层原理深度分析
- 3.1 加锁原理
- 3.2 解锁原理
- 3.3 看门狗(WatchDog)机制
- 3.3.1 看门狗的工作原理
- 3.3.2 续期脚本
- 3.4 可重入实现原理
- 四、总结
前言
在微服务架构和分布式系统中,分布式锁是保证数据一致性的重要手段。Redis作为高性能的内存数据库,天然适合实现分布式锁。而Redisson作为Redis的Java客户端,不仅提供了完善的分布式锁实现,还引入了看门狗(WatchDog)机制来解决锁续期问题。
一、分布式锁的基本概念
1.1 什么是分布式锁
分布式锁是在分布式环境下,多个进程或线程对共享资源进行互斥访问的一种机制。它需要满足以下特性:
- 互斥性:同一时刻只能有一个进程持有锁
- 可重入性:同一线程可以多次获取同一把锁
- 阻塞与非阻塞:获取不到锁时的处理策略
- 容错性:具备自动释放锁的能力
1.2 为什么选择Redisson
相比于直接使用Redis命令实现分布式锁,Redisson提供了以下优势:
- 自动续期:通过看门狗机制避免业务执行时间过长导致的锁自动释放
- 可重入实现:支持同一线程多次获取同一把锁
- 阻塞等待:提供tryLock等方法支持超时等待
- Lua脚本:保证原子性操作
二、Redisson分布式锁的使用
2.1 基本使用示例
@Service
public class DistributedLockService {@Autowiredprivate RedissonClient redissonClient;public void processWithLock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);try {// 尝试获取锁,等待时间10秒,锁自动释放时间30秒boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);if (isLocked) {System.out.println("获取锁成功,开始处理业务逻辑");// 执行业务逻辑doBusinessLogic();System.out.println("业务逻辑处理完成");} else {System.out.println("获取锁失败");}} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println("获取锁被中断");} finally {// 释放锁if (lock.isHeldByCurrentThread()) {lock.unlock();System.out.println("锁已释放");}}}private void doBusinessLogic() {try {// 模拟业务处理时间Thread.sleep(5000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}
2.2 可重入锁示例
public class ReentrantLockDemo {private final RedissonClient redissonClient;private final RLock lock;public ReentrantLockDemo(RedissonClient redissonClient) {this.redissonClient = redissonClient;this.lock = redissonClient.getLock("reentrant:lock");}public void method1() {lock.lock();try {System.out.println("执行方法1");method2(); // 可重入调用} finally {lock.unlock();}}public void method2() {lock.lock(); // 同一线程再次获取锁try {System.out.println("执行方法2");method3();} finally {lock.unlock();}}public void method3() {lock.lock(); // 同一线程第三次获取锁try {System.out.println("执行方法3");} finally {lock.unlock();}}
}
2.3 看门狗机制的自动续期
public class WatchDogDemo {private final RedissonClient redissonClient;public WatchDogDemo(RedissonClient redissonClient) {this.redissonClient = redissonClient;}public void longRunningTask() {RLock lock = redissonClient.getLock("watchdog:lock");try {// 不指定锁的过期时间,启用看门狗机制lock.lock();System.out.println("开始执行长时间任务");// 模拟长时间运行的任务(超过默认锁过期时间30秒)for (int i = 0; i < 10; i++) {Thread.sleep(10000); // 每次休眠10秒System.out.println("任务进行中... " + (i + 1) * 10 + "秒");}System.out.println("长时间任务执行完成");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}
}
三、底层原理深度分析
3.1 加锁原理
Redisson使用Lua脚本来保证加锁操作的原子性,核心脚本如下:
-- 加锁脚本
if (redis.call('exists', KEYS[1]) == 0) thenredis.call('hset', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) thenredis.call('hincrby', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
return redis.call('pttl', KEYS[1]);
这个脚本的执行逻辑:
- KEYS[1]:锁的名称
- ARGV[1]:锁的过期时间(毫秒)
- ARGV[2]:线程标识(UUID + 线程ID)
执行流程:
- 如果锁不存在,创建锁并设置过期时间
- 如果锁存在且是当前线程持有,重入计数加1
- 否则返回锁的剩余存活时间
3.2 解锁原理
解锁同样使用Lua脚本保证原子性:
-- 解锁脚本
if (redis.call('exists', KEYS[1]) == 0) thenreturn 1;
end;
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) thenreturn nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) thenredis.call('pexpire', KEYS[1], ARGV[2]);return 0;
elseredis.call('del', KEYS[1]);redis.call('publish', KEYS[2], ARGV[1]);return 1;
end;
解锁逻辑:
- 锁不存在直接返回
- 当前线程不持有锁返回null
- 重入计数减1,如果大于0重新设置过期时间
- 如果计数为0,删除锁并发布解锁消息
3.3 看门狗(WatchDog)机制
看门狗机制是Redisson的核心特性,解决了业务执行时间不确定导致的锁提前释放问题。
3.3.1 看门狗的工作原理
实现原理概览
看门狗机制本质上是一个 后台定时任务,它:
- 在成功获取锁后自动启动
- 每隔一段时间检查当前线程是否仍然持有锁
- 如果是,则向 Redis 发送命令延长锁的过期时间(TTL)
- 直到锁被显式释放(unlock())或客户端断开
3.3.2 续期脚本
-- 续期脚本
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) thenredis.call('pexpire', KEYS[1], ARGV[1]);return 1;
end;
return 0;
看门狗机制的关键点:
- 续期时机:每隔
lockLeaseTime/3
时间执行一次续期 - 续期条件:只有当前线程持有锁时才能续期成功
- 自动停止:锁释放时自动停止看门狗任务
- 默认时间:默认锁存活时间为30秒,续期间隔为10秒
3.4 可重入实现原理
Redisson使用Hash数据结构实现可重入:
锁的数据结构:
KEY: lock:mylock
VALUE: {"8743c9c0-0795-4907-87fd-6c719a6b4586:1": 3
}
- KEY:锁的名称
- Hash Field:客户端ID + 线程ID
- Hash Value:重入次数
这种设计的优势:
- 通过Hash结构存储线程标识和重入次数
- 原子性操作保证数据一致性
- 支持多线程并发访问不同的锁
四、总结
Redisson分布式锁通过以下几个关键机制实现了高可用、高性能的分布式锁方案:
- Lua脚本保证原子性:加锁、解锁、续期操作都通过Lua脚本实现原子性
- 看门狗机制解决续期问题:自动续期避免业务执行时间过长导致的锁丢失
- Hash结构实现可重入:使用Redis Hash存储线程信息和重入次数
- 发布订阅优化性能:通过Redis的pub/sub机制减少客户端轮询