知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
在 Redis 集群模式下,加锁和解锁操作需要确保针对同一个键(即锁的 Key)的请求被路由到正确的集群节点。Redis 集群通过 哈希槽(Hash Slot) 分配机制实现数据分片,而 Redisson 等客户端库则通过以下策略保证加锁和解锁操作能定位到同一台机器:
一、Redis 集群的数据分片规则
-
哈希槽分配:
- Redis 集群将数据划分为 16384 个槽(Slot),每个节点负责一部分槽。
- 键的所属槽由
CRC16(key) % 16384
计算得出。 - 例如,锁的 Key 为
my_lock
,其槽号为CRC16("my_lock") % 16384
。
-
客户端路由:
- 客户端通过集群配置知道每个槽所在的节点。
- 执行命令时,客户端直接向负责该槽的节点发起请求。
二、加锁与解锁的定位机制
1. 加锁时的节点定位
- 步骤 :
- 客户端计算锁 Key(如
my_lock
)的哈希槽。 - 根据集群配置找到负责该槽的节点(假设为节点 A)。
- 向节点 A 发送加锁命令(如
SET my_lock uuid:threadId NX PX 30000
)。
- 客户端计算锁 Key(如
- 关键点 :
- 所有对
my_lock
的请求(包括后续解锁)都会由节点 A 处理。
- 所有对
2. 解锁时的节点定位
- 步骤 :
- 客户端再次计算
my_lock
的哈希槽,定位到同一节点 A。 - 向节点 A 发送解锁的 Lua 脚本(需验证客户端标识)。
- 客户端再次计算
- 原子性保障 :
- Lua 脚本在节点 A 上原子化执行,确保解锁操作的安全。
3. 节点故障时的处理
- 集群自动转移 :
- 如果节点 A 宕机,且集群启用了故障转移(如从节点晋升),客户端会:
- 收到
MOVED
或ASK
重定向错误。 - 根据错误信息重新向新主节点发起请求。
- 收到
- 如果节点 A 宕机,且集群启用了故障转移(如从节点晋升),客户端会:
- Redisson 的容错 :
- Redisson 自动处理重定向,无需用户干预。
三、Redisson 在集群模式下的优化
1. 一致哈希路由
- Redisson 在客户端缓存 槽与节点的映射关系,避免每次请求都查询集群配置。
- 当集群拓扑变化时(如节点增减),客户端通过
CLUSTER SLOTS
命令更新缓存。
2. 多锁模式(MultiLock)
-
场景 :需跨多个键加锁(如
lock1
、lock2
分布在不同的节点)。 -
实现 :
javaRLock lock1 = redisson.getLock("lock1"); RLock lock2 = redisson.getLock("lock2"); RLock multiLock = redisson.getMultiLock(lock1, lock2); multiLock.lock(); // 依次向不同节点加锁
3. 看门狗(WatchDog)续期
- 问题:集群模式下锁的过期时间可能因节点时间不同步导致提前释放。
- 解决 :Redisson 后台线程定期向原节点发送续期命令(
PEXPIRE
)。
四、关键注意事项
-
避免跨槽事务 :
-
Redis 集群不支持跨槽事务,确保锁 Key 的哈希槽相同(可通过
{}
强制路由):java// 使用 Hash Tag 确保所有 Key 落到同一槽 RLock lock = redisson.getLock("{user}:lock"); // 如 "user123:lock"
-
-
网络分区风险 :
- 脑裂可能导致多个客户端同时持有锁,需配置
cluster-require-full-coverage no
。
- 脑裂可能导致多个客户端同时持有锁,需配置
-
性能监控 :
-
使用
CLUSTER KEYSLOT
命令检查锁 Key 的槽分布:bashCLUSTER KEYSLOT "my_lock"
-
五、与其他方案的对比
方案 | 定位机制 | 优点 | 缺点 |
---|---|---|---|
Redis 集群 | 哈希槽 + 客户端路由 | 原生支持,性能高 | 需处理重定向和故障转移 |
Redisson 代理 | 封装集群路由逻辑 | 对开发者透明 | 依赖客户端实现 |
Twemproxy 分片 | 代理层计算分片 | 解耦客户端 | 单点瓶颈,不支持所有命令 |
六、总结
在 Redis 集群模式下,加锁和解锁的节点定位依赖以下核心机制:
- 哈希槽分配 :通过
CRC16(key) % 16384
固定锁 Key 的归属节点。 - 客户端路由:Redisson 等库自动处理槽映射和重定向。
- 原子性脚本:解锁时通过 Lua 脚本在目标节点上验证和执行。
最佳实践:
- 使用 Hash Tag (如
{resource}:lock
)强制锁 Key 分配到同一槽。 - 启用 WatchDog 防止锁因网络抖动或 GC 暂停超时。
- 监控集群状态,避免节点负载不均。