redisson

发布时间:2026/10/23 2:33:36
redisson
redisson核心架构分层分布式锁RLock实现原理lua脚本加锁lua脚本解锁Watch Dog自动续期Redisson核心执行流程哈希槽 哈希标签Redis Cluster 哈希槽位计算 Redisson 集群路由原理Redisson 优势对比原生 Jedis/LettuceRedis集群3主3从架构核心架构分层1. 业务层RLOCKRMapRQueueRateLimiter。。。 ↓ 2. 命令封装层RedisCommandsExecutor统一分发命令 ↓ 3. Netty网络层连接池channel编解码重连 ↓ 4. Redis服务端单机/哨兵/集群分布式锁RLock实现原理# 1.锁底层命令Lua脚本原子保证 Redisson加锁续锁释放锁全部使用Lua脚本能够保证多条Redis命令原子执行。 (Hash数据结构 Lua脚本 WatchDog看门狗线程 PubSub发布订阅阻塞) # 2.数据结构Hash key锁名称 lock:stock fieldUUID:线程ID区分不同客户端 线程 value重入次数 优势支持锁可重入同一个线程多次 lock 不会死锁lua脚本加锁-- KEYS[1]锁key-- ARGV[1]过期时间-- ARGV[2]当前线程唯一标识uuid:threadId-- 判断key不存在直接写入锁设置过期时间ifredis.call(exists,KEYS[1])0thenredis.call(hset,KEYS[1],ARGV[2],1);redis.call(pexpire,KEYS[1],ARGV[1]);returnnil;end;-- 锁存在且是当前持有线程重入计数1重置过期时间ifredis.call(hexists,KEYS[1],ARGV[2])1thenredis.call(hincrby,KEYS[1],ARGV[2],1);redis.call(pexpire,KEYS[1],ARGV[1]);returnnil;end;-- 被其他线程占用返回锁剩余过期时间returnredis.call(pttl,KEYS[1]);lua脚本解锁-- 只能释放自己持有的锁不会删除其他线程的锁-- 解锁后发布 Pub/Sub 消息实现公平锁、可阻塞锁唤醒机制。-- KEYS[1] 锁key-- ARGV[1] 客户端线程标识-- 判断锁持有者是否自己不是直接返回ifredis.call(hexists,KEYS[1],ARGV[1])0thenreturnnil;end;-- 重入计数-1localcounterredis.call(hincrby,KEYS[1],ARGV[1],-1);ifcounter0then-- 还有重入次数只重置过期不删锁redis.call(pexpire,KEYS[1],ARGV[2]);returnnil;end;-- 计数为0删除锁redis.call(del,KEYS[1]);-- 发布解锁消息唤醒阻塞等锁的线程-- KEYS[2] publish 的频道名称channel-- ARGV[1] 推送的消息内容redis.call(publish,KEYS[2],ARGV[1]);return1;Watch Dog自动续期# 解决问题 解决 “业务逻辑未执行完成锁已过期释放”的经典问题。 # 触发条件 调用无过期时间的lock方法时会自动启动看门狗如果手动指定了锁持有时间则不会启动看门狗到期自动释放。 # 默认过期时间 看门狗默认的锁过期时间是30秒可通过lockWatchdogTimeout 全局配置 # 续期逻辑 每隔1/3过期时间30/310s,Redisson会在后台执行依次续期命令重新将锁的过期时间设置为30s # 终止条件 线程主动释放锁客户端宕机无法续期锁到期自动释放Redisson核心执行流程1.业务调用rLock.lock() ↓ 2.Redisson生成当前客户端唯一UUID 当前线程ID ↓ 3.构造加锁Lua脚本通过Netty发送到Redis ↓ 4.Redis执行Lua返回nil加锁成功ttl被占用 ↓ 5.加锁成功启动看门狗定时续期任务 ↓ 6.加锁失败订阅锁频道阻塞等待解锁消息 ↓ 7.收到解锁Pub/Sub通知重新执行抢锁逻辑 ↓ 8.业务执行完调用unlock执行释放锁Lua脚本删除定时续期任务哈希槽 哈希标签# Hash Slot哈希槽 哈希槽是Redis集群用来分配数据分片的虚拟分区是数据分片的最小单位是Redis集群实现分布式存储的核心机制。 Redis集群固定划分16384个哈希槽0~16383所有键都会映射到这16384个槽里再把不同槽分配给不同主节点实现数据拆分。 # 取键名做 CRC16 校验得到一个 16 位数值, 数值对 16384 取模结果就是这个键所属的哈希槽编号 公式 slot CRC16(key) % 16384 举个例子key user:1001 CRC16 计算后取模算出结果是 568那这个键就固定存在 568 号哈希槽 对应的主节点。 # 哈希槽的作用 分片存储扩容均衡 集群多个主节点管理员手动把 16384 个槽拆分分配给各节点。 比如 3 节点集群 节点 A0~5460 节点 B5461~10922 节点 C10923~16383 新增节点时只需要迁移一部分哈希槽不用全量迁移数据扩容平滑。 数据迁移最小粒度是 “槽” 迁移时以整个哈希槽为单位批量转移一批 key不是一条一条迁移效率高。 路由定位 key 客户端随便连任意节点节点计算 key 对应的槽如果槽不归自己管理就返回 MOVED 重定向客户端再去对应节点操作。 # 哈希标签Hash Tag 控制 key 到同一个槽 业务场景希望一批关联 key 落在同一个槽方便批量操作 / 事务。 规则key 中 {} 包裹的部分参与 CRC16 计算只有大括号内内容参与哈希。 例 order{100}:info order{100}:goods 两者大括号里都是100CRC16 结果一致分到同一个哈希槽可以在同一节点执行事务如msetnx批量操作。Redis Cluster 哈希槽位计算 Redisson 集群路由原理# 一、基础前置Redis Cluster 总槽位规则 Redis Cluster 一共固定 16384 个槽位0 ~ 16383所有数据全部存放在这 16384 个 slot 中。 集群中每个主节点负责一部分 连续/离散槽位比如 节点 A0–5460 节点 B5461–10922 节点 C10923–16383 写入 / 读取 key 时必须先算出 key 属于哪个 slot再路由到持有该 slot 的主节点不能随便发给任意节点否则会返回 *MOVED重定向错误*。 # 二、key 计算 slot 的官方算法 1.提取 key 的有效部分支持哈希标签 {} 2.CRC16 哈希取模 16384 slot CRC16(key) % 16384 ## 哈希标签 HashTag解决跨槽批量操作 如果 key 包含 {xxx}只对大括号内部字符串计算 CRC16忽略外部字符 user:{100}:info → 只计算 100 order:{200}:pay → 只计算 200 作用把一批关联数据强制落到同一个 slot支持 mget、mset、 pipeline、事务等批量操作否则跨槽不支持批量原子操作。 示例 {shop}:goods1、{shop}:goods2 计算出同一个 slot可批量操作 # 三、Redis 原生流程客户端直连集群 1 客户端发送命令到任意集群节点 2 节点算出 key 对应 slot判断当前节点是否负责该 slot 3 是直接执行命令返回结果 4 否返回 MOVED slot ip:port告诉客户端真正节点地址 5 客户端收到 MOVED重新连接目标节点重试命令 (缺点频繁重定向网络损耗大) # 四、Redisson 优化路由机制重点 Redisson 不会每次都等服务端返回 MOVED本地缓存槽位节点映射表直接本地算出节点一步到位路由 ## 步骤1初始化拉取槽位分配表 Redisson启动时随机连接任意集群点执行CLUSTER SLOTS Redisson返回完整信息每隔slot区间对应主节点IP端口从节点。 Redisson在本地内存构建一张映射表slot → RedisNode连接、地址、channel ## 步骤2本地计算key对应slot 客户端执行get/set的时候 Redisson内部执行CRC16(key)%16384算出slot 查询本地缓存映射表直接拿到该slot对应的主节点连接 通过Netty连接池把命令直接发给对应主节点执行全称不需要redis服务端返回MOVED省去依次往返网络开销 ## 步骤3槽位变更自动刷新映射表 两种场景触发本地缓存更新 1.收到 MOVED 响应 槽迁移完成永久归属新节点 节点发生槽位迁移扩容、分片调整本地缓存失效Redis 返回 MOVED。 Redisson 立刻重新执行 CLUSTER SLOTS刷新本地 slot-node 映射再重试命令。 2.收到 ASK 响应迁移中临时状态 槽位正在从节点 A 迁移到节点 B数据两边都不完整Redis 返回 ASK。 Redisson 先发送 ASKING 命令到目标新节点再执行业务命令不更新本地缓存迁移完成后才会出MOVED ## 步骤 4读写分离路由 Redisson 支持读从节点 写操作强制路由 slot 对应的主节点 读操作可配置路由策略 读请求提供多种策略可配置 MASTER只读主 SLAVE只读从 MASTER_SLAVE主从均衡 SLAVE_RANDOM随机选一台从节点分摊主节点压力 Redisson 内部会缓存每个主的从节点列表读请求自动分发。 # 五、特殊场景多 key 跨 slot 怎么处理 普通多条 key无 HashTag分到不同 slot 原生 Redis 不支持 mset/mget直接报错。 ## Redisson 解决方案 方案 1用 HashTag 强制所有 key 落到同一个 slot正常批量操作 如user{test1000}:key001 user{test1000}:key002 方案 2RedissonMultiLock / 分批并发请求分别路由各自节点聚合结果 # 六、故障自动转移原理主从切换 哨兵 / 集群内部完成主从切换后旧主下线、从晋升新主 Redisson 发送命令访问旧主会触发连接失败 / MOVED 自动重新拉取 CLUSTER SLOTS更新 slot 与主节点映射 自动关闭失效旧连接池为新主创建连接池业务代码无需改动 # 一句话总结整条链路 业务代码传入 key → Redisson 本地 CRC16 算出 slot → 查询内存缓存的「slot - 节点映射表」 → 获取对应节点 Netty 连接 → 直接发送命令到持有该槽位的Redis主节点执行 (槽位迁移时自动刷新本地映射表) → 返回结果序列化后交给业务(全程无 MOVED重定向 跳转一次网络往返)Redisson 优势对比原生 Jedis/Lettuce1.Redisson拥有开箱即用分布式组件原生客户端需要手写分布式锁极易出现死锁、不可重入、误删锁问题 2.Redisson内置看门狗、RLock可重入锁、Pub/Sub 阻塞等待。 3.Redisson对redis集群路由封装完善 4.Redisson使用Netty作为网络层异步高性能连接池、自动重连Redis集群3主3从架构# Redis Cluster 3 主 3 从架构流程图 客户端Redisson/JedisCluster ↓ 自动缓存16384槽映射自动转发请求 ┌─────────────┬─────────────┬─────────────┐ │ Master-A │ Master-B │ Master-C │ 3台主节点分配哈希槽 │ 槽0~5460 │ 槽5461~10922│ 槽10923~16383│ │ ↓同步 │ ↓同步 │ ↓同步 │ Replica-A1 │ Replica-B1 │ Replica-C1 │ 对应从节点备份、故障顶替 └─────────────┴─────────────┴─────────────┘ ↓集群总线16379端口 节点互相心跳检测、同步元数据(节点IDIP端口主从对应关系自己持有哪些哈希槽槽迁移进度) # 数据分片存储流程哈希槽分配 1. 客户端操作 key user:1001 2. 集群计算CRC16(user:1001) % 16384 → 得到槽位编号 3. 根据槽位映射路由到对应 Master 节点读写 4. Master 同步数据到自身 Replica 从节点做备份