Redisson作为Redis Java客户端中的分布式解决方案佼佼者,其分布式锁实现被广泛应用于生产环境。以下从底层设计到源码实现进行全面剖析。
一、核心架构设计
1. 整体架构图
graph LR
A[客户端] --> B[RLock接口]
B --> C[RedissonLock]
C --> D[Redis命令执行]
D --> E[Lua脚本]
E --> F[Redis集群]
C --> G[WatchDog]
G --> C
2. 核心组件
|------------------|---------|---------------|
| 组件 | 作用 | 关键特性 |
| RLock接口 | 定义锁行为规范 | 扩展Java Lock接口 |
| RedissonLock | 基础锁实现 | 封装Redis交互逻辑 |
| Lua脚本 | 原子操作执行 | 保证操作原子性 |
| WatchDog | 锁续期守护线程 | 防止业务未完成锁过期 |
| PubSub | 锁释放通知 | 实现阻塞等待机制 |
二、加锁流程深度解析
1. 加锁核心Lua脚本
-- KEYS[1]: 锁key名
-- ARGS[1]: 锁超时时间(毫秒)
-- ARGS[2]: 客户端唯一标识(UUID+线程ID)
-- 1. 检查锁是否存在
if (redis.call('exists', KEYS[1]) == 0) then
-- 2. 不存在则加锁
redis.call('hset', KEYS[1], ARGV[2], 1);
-- 3. 设置过期时间
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 4. 锁已存在,检查是否当前线程持有
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
-- 5. 重入次数+1
redis.call('hincrby', KEYS[1], ARGV[2], 1);
-- 6. 刷新过期时间
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 7. 返回剩余生存时间(毫秒)
return redis.call('pttl', KEYS[1]);
2. 加锁流程时序图
sequenceDiagram
participant Client
participant Redisson
participant Redis
Client->>Redisson: tryLock(10, TimeUnit.SECONDS)
Redisson->>Redis: 执行Lua脚本
alt 锁不存在或可重入
Redis-->>Redisson: 加锁成功
Redisson->>Redisson: 启动WatchDog
Redisson-->>Client: 返回true
else 锁被占用
Redis-->>Redisson: 返回TTL
Redisson->>Redis: 订阅锁释放频道
Redis--xRedisson: 等待通知(异步)
Redisson->>Client: 返回false或阻塞
end
3. 关键参数说明
|---------------------|--------------|-----------------|
| 参数 | 作用 | 推荐设置 |
| lockWatchdogTimeout | WatchDog检查间隔 | 30000ms(默认) |
| leaseTime | 锁持有时间 | 不设置(启用WatchDog) |
| waitTime | 获取锁等待时间 | 根据业务调整 |
三、锁续期机制(WatchDog)
1. 实现原理
// RedissonLock类中
protected void scheduleExpirationRenewal(String threadId) {
// 每10秒执行一次续期
Timeout task = commandExecutor.getConnectionManager()
.newTimeout(new TimerTask() {
public void run(Timeout timeout) {
// 执行续期Lua脚本
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
return;
}
if (res) {
// 递归调用实现定期执行
scheduleExpirationRenewal(threadId);
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}
2. 续期Lua脚本
-- KEYS[1]: 锁key
-- ARGV[1]: 续期时间
-- ARGV[2]: 客户端标识
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('pexpire', KEYS[1], ARGV[1]);
return 1;
end;
return 0;
3. 异常处理机制
- 网络中断:重试3次后放弃续期
- Redis故障 :通过
ConnectionListener
检测连接状态 - 客户端崩溃:依赖Redis的key自动过期
四、解锁机制剖析
1. 解锁Lua脚本
lua
-- KEYS[1]: 锁key
-- KEYS[2]: 锁释放频道
-- ARGV[1]: 释放消息
-- ARGV[2]: 客户端标识
-- ARGV[3]: 锁超时时间
-- 1. 检查锁是否存在
if (redis.call('hexists', KEYS[1], ARGV[2]) == 0) then
return nil;
end;
-- 2. 减少重入计数
local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1);
if (counter > 0) then
-- 3. 仍有重入则刷新过期时间
redis.call('pexpire', KEYS[1], ARGV[3]);
return 0;
else
-- 4. 完全释放锁
redis.call('del', KEYS[1]);
-- 5. 发布释放消息
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
return nil;
2. 解锁流程
- 检查当前线程是否持有锁
- 减少重入计数器
- 计数器归零时删除key
- 通过PubSub发布解锁消息
- 取消WatchDog续期任务
五、高级特性实现
1. 公平锁实现
RLock fairLock = redisson.getFairLock("fairLock");
实现原理:
- 使用Redis列表维护等待线程队列
- 通过ZSet记录线程超时时间
- 采用"先到先服务"策略
2. 读写锁
RReadWriteLock rwLock = redisson.getReadWriteLock("rwLock");
rwLock.readLock().lock();
rwLock.writeLock().lock();
存储结构:
# 写锁
rwLock: { "mode": "write", "UUID_01:threadId_1": 1 }
# 读锁
rwLock: { "mode": "read", "UUID_01:threadId_1": 3, "UUID_02:threadId_1": 1 }
3. 联锁(MultiLock)
RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock multiLock = redisson.getMultiLock(lock1, lock2);
实现特点:
- 所有锁必须全部获取成功
- 采用相同的leaseTime
- 整体视为一个原子操作
六、生产环境最佳实践
1. 参数调优建议
Config config = new Config();
config.setLockWatchdogTimeout(30000); // 看门狗超时
config.useClusterServers()
.setPingConnectionInterval(5000) // 心跳检测
.setTimeout(3000); // 命令超时
2. 异常处理模板
RLock lock = redisson.getLock("resourceLock");
try {
if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("锁获取中断");
} catch (Exception e) {
// 记录监控指标
monitor.recordLockFailure();
throw e;
}
3. 监控指标采集
|--------|------------------|-----------|
| 指标 | 采集方式 | 告警阈值 |
| 锁等待时间 | Micrometer Timer | >1s |
| 锁竞争频率 | Redis慢查询日志 | >50次/秒 |
| 锁续期失败 | WatchDog日志 | 连续3次失败 |
| 锁泄漏 | 未释放锁统计 | 存活>10min |
七、常见问题解决方案
1. 锁续期失败
场景 :Redis节点故障导致续期中断
方案:
- 启用Redisson的
retryInterval
参数 - 配置集群模式自动切换节点
2. 网络分区问题
场景 :客户端与Redis集群断开连接
方案:
- 设置合理的
timeout
参数 - 实现降级策略(如本地缓存)
3. 锁竞争激烈
场景 :大量线程等待同一把锁
方案:
- 引入锁分段(如将锁拆分为lock_1, lock_2...)
- 使用
tryLock
替代lock
避免长时间阻塞
Redisson通过精心设计的Lua脚本、WatchDog机制和灵活的配置选项,提供了生产级的分布式锁实现。理解这些底层原理有助于在复杂分布式系统中正确使用和问题排查。