解析Redisson核心原理与设计思想
Redisson 是 Redis 官方推荐的 Java 分布式协调组件,它将 Redis 的丰富数据结构扩展为 Java 熟悉的并发对象(如分布式锁、Map、队列、信号量),并提供了完整的集群、哨兵、主从支持。本文将深入剖析 Redisson 的架构基石、分布式锁的完整链路、看门狗机制、异步编程模型以及如何通过 Lua 保证原子性,以"好看懂、能落地"的方式展示其原理。
放一张核心加锁逻辑总图

1. 架构全景:异步 · 原子 · 高可用
Redisson 的底层通信完全基于 Netty ,所有 Redis 命令执行都是异步非阻塞的,通过 RFuture 抽象将回调转换为 Java 的 CompletionStage 或直接的异步接口。这种设计让它天然支持高吞吐。
整体架构可概括为三个核心支柱:
- 异步引擎:Netty + 线程池 + 连接池,命令执行零阻塞,支持发布/订阅事件流。
- 原子脚本 :所有复合操作(如加锁、续期、解锁)均通过 Lua 脚本 在 Redis 服务端一次性执行,杜绝并发竞争。
- 对象映射 :将 Redis 的 String、Hash、List、ZSet 等数据结构,封装为 Java 友好的
RMap、RLock、RQueue等接口,提供本地缓存、监听回调等增强。
设计哲学:尽量将逻辑移到 Redis 内部执行(Lua),客户端只负责发起指令和监听结果,避免分布式共识的复杂开销。
2. 分布式锁的完整解剖
RLock 是 Redisson 中最广泛使用的特性,其实现远非简单的 SETNX 可比,它要解决可重入、自动续期、非阻塞等待、集群安全四大难题。
2.1 加锁:一个 Lua 脚本定乾坤
加锁的核心是一段精心设计的 Lua 脚本(简化版如下):
lua
-- KEYS[1] 锁名 , ARGV[1] 客户端标识(UUID:线程ID) , ARGV[2] 锁租约时间(ms)
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hincrby', KEYS[1], ARGV[1], 1);
redis.call('pexpire', KEYS[1], ARGV[2]);
return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[1]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[1], 1);
redis.call('pexpire', KEYS[1], ARGV[2]);
return nil;
end;
return redis.call('pttl', KEYS[1]);
原理拆解:
- 锁在 Redis 中存储为一个 Hash ,键为锁名,字段为
客户端UUID:线程ID,值为重入次数。 - 首次加锁:Hash 不存在,直接创建并设置租约时间(默认30秒)。
- 重入加锁:判断当前客户端是否已持有锁(hexists),是则重入次数+1并刷新租约。
- 如果锁被其他客户端持有,返回剩余过期时间(pttl),客户端据此进行等待。
这种方式天然支持了可重入 和强一致性------由 Redis 单线程串行化执行脚本。
2.2 看门狗:锁的自动续命
如果业务执行时间超过锁的租约(30秒),锁会被自动释放,造成并发问题。Redisson 提供了经典的 Watch Dog 机制:
-
加锁成功后,客户端会启动一个 后台定时任务 (基于 Netty 的
HashedWheelTimer时间轮),每隔 租约/3 (默认 10 秒)触发一次续期。 -
续期执行一条简单的 Lua 脚本:
luaif (redis.call('hexists', KEYS[1], ARGV[1]) == 1) then redis.call('pexpire', KEYS[1], ARGV[2]); return 1; end; return 0;该脚本判断锁是否仍被同一客户端持有,是则将过期时间重置为租约值。
-
当锁被主动释放或客户端宕机,续期任务会因锁不存在而自动失效,不会无谓续期。
注意 :如果指定了
tryLock(waitTime, leaseTime, unit)中的leaseTime,则不会启动看门狗,锁会在到达时间后自动释放,适合短任务。
2.3 解锁与等待唤醒:Pub/Sub 而非轮询
解锁同样使用 Lua 脚本保证原子性:
lua
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then return nil; end;
local count = redis.call('hincrby', KEYS[1], ARGV[1], -1);
if (count > 0) then
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[3]); -- 通知等待线程
return 1;
end;
当锁的 Hash 被删除时,Redis 会向频道 redisson_lock__channel:{锁名} 发送 解锁消息 ,正在 lock() 上阻塞的其他线程会订阅该频道,收到消息后立即竞争锁。这比自旋轮询节省大量网络开销,保障了公平性与低延迟。
2.4 RedLock:应对集群分区的增强
在哨兵或集群模式下,Redisson 实现了 RedLock 算法:它会在所有独立的 master 节点上依次尝试获取锁,当在大多数节点(N/2+1)上成功且总耗时不超预算时才算获取成功。这防止了主从切换时的锁丢失,但也以增加复杂度和延迟为代价,适用于极高安全需求的场景。