07.集群
文章目录
问题: 高可用架构之集群模式:Redis Cluster 如何通过哈希槽(16384 个 slot)实现数据分片?在集群模式下,多 key 操作(如 mget)存在什么限制?如何通过 hash tag 绕过?
👇 点击展开/折叠 面试背诵模板(建议先背这个)
哈希槽的数据分布与节点映射
- 每个 key 的 slot 计算:
HASH_SLOT = CRC16(key) & 16383(等价于% 16384) - 每个节点负责一段连续的 slot 区间(如 Node A: 0-5000, Node B: 5001-10000, Node C: 10001-16383)
- 节点扩缩容:只需将部分 slot 从旧节点迁移到新节点,无需对所有 key 进行全局 rehash(一致性哈希也类似,但 slot 机制更简单)
- 每个节点存储其他节点的 slot 分布信息,客户端通过
CLUSTER SLOTS命令获取集群拓扑并缓存,实现智能路由
为什么是 16384 个 slot?
- 恰好是 2^14,位运算效率高(
& 16383比% 16384更快) - 心跳包(Gossip 消息)需携带节点所负责的 slot 位图:
16384 bits = 2KB,若改用 65536 个 slot,位图为 8KB,网络开销更大 - 官方设计权衡:满足中等规模集群(官方建议 ≤ 1000 节点)的同时保证心跳包足够小
多 key 操作的跨 slot 限制详解
Redis Cluster 要求涉及多个 key 的命令(如 MGET、MSET、DEL、事务 MULTI/EXEC、SUNION 等)所有 key 必须属于同一个 slot,否则服务端返回 CROSSSLOT 错误。
示例:
1 | # 假设 key1 在 slot 1234,key2 在 slot 5678(不同节点) |
为何有此限制?
- 不同 slot 可能分布在不同节点,多 key 操作需要同时访问多个节点,协调开销极大,破坏原子性保证
- 分布式事务实现复杂,Redis 为保持简单,直接禁止跨 slot 的原子多 key 操作
例外情况:MGET/MSET 等在某些语言客户端中可能会被拆分为多个单 key 命令串行执行,但这只是客户端层面的补偿,不保证原子性。
Hash Tag 的使用方式与示例
Hash Tag 是一种显式指定哈希计算目标的机制:只对 key 中 {} 包裹的部分进行 CRC16 计算。
1 | # 普通 key:对整个 "user:1001:name" 计算哈希 → slot 可能分散 |
多个花括号的处理规则:若 key 中包含多个 {},只取第一个花括号内的内容作为哈希标签(如 {user}1001:{profile} → 仅 user 参与哈希)。
使用场景:
- 将用户相关数据(档案、订单、购物车)强制部署在同一节点
- 实现跨 key 事务(需保证相关数据在同一 slot)
- 配合
SORT、SUNION等集合操作,确保操作合法
集群模式下客户端如何工作?
智能客户端路由:
- 首次连接时执行
CLUSTER SLOTS获取 slot 到节点的映射并缓存 - 执行命令时本地计算 slot,直接连接对应节点
- 若节点返回
MOVED错误(slot 已迁移),客户端更新缓存并重试 - 若返回
ASK(slot 正在迁移中),客户端向目标节点发送ASKING命令后重试
JDBC 类比:类似 ShardingSphere 的 SQL 解析与分片路由,底层逻辑相同。
集群模式下的数据迁移过程
当集群新增节点或删除节点时,需要进行 slot 迁移(resharding):
- 迁移单位:slot(每次迁移一个 slot 中的全部 key)
- 迁移指令:
CLUSTER SETSLOT <slot> MIGRATING <target_node_id> - 迁移过程:源节点和新节点建立同步,将 key 逐个发送到目标节点(通过
MIGRATE命令) - 迁移期间:客户端的请求会返回
ASK或MOVED错误引导重试,保证服务不中断
故障转移与高可用机制
- 集群中每个节点与其他节点保持 TCP 连接,通过 Gossip 协议交换状态信息(ping/pong 消息)
- 节点可配置主从复制(
cluster replicate <node_id>),为 master 节点添加 slave - 当主节点失联(超过
cluster-node-timeout且多数节点确认)时,slave 发起故障转移:- 从所有 slave 中选举新 master(Raft 风格投票,比较偏移量)
- 新 master 接管该节点负责的 slot
- 集群恢复正常状态
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 LGQ的博客!