Redis中的哈希槽怎么理解

Redis 哈希槽(Hash Slot)详解:从零理解 Redis Cluster 的数据分片核心

在 Redis Cluster(集群模式)中,哈希槽 是实现数据分片、高可用与线性扩展的基石。很多开发者刚接触集群时,对"16384 个槽"、"槽位路由"、"节点宕机如何迁移"等概念感到困惑。本文将用通俗的语言 + 流程图 + 实例,彻底讲清哈希槽的设计思想与工作原理。


一、为什么需要哈希槽?

单个 Redis 实例的内存、吞吐量有上限。为了存储更多数据、支撑更高 QPS,自然想到 将数据分散到多个 Redis 节点 ------ 这就是分布式分片。

分片面临三个核心问题:

  1. 数据如何均匀分布 到不同节点?
  2. 客户端如何知道 某个 key 应该去哪个节点访问?
  3. 节点增减/故障 时如何尽量减少数据迁移?

哈希槽正是 Redis Cluster 解决上述问题的答案。


二、哈希槽的基本概念

Redis Cluster 将整个数据空间固定划分为 16384 个哈希槽 (Hash Slot),编号从 016383

核心公式

复制代码
hash_slot = CRC16(key) % 16384
  • CRC16(key) 对 key 进行循环冗余校验,得到一个 16 位整数(0~65535)
  • 对 16384 取模,结果必然落在 0~16383 之间

为什么是 16384?

Redis 作者解释过:16384 是一个适中的数字,每个槽的元数据用 2KB 的 bitmap 即可描述(16384 bits = 2KB),且能满足绝大多数集群规模(最大 1000 节点,平均每个节点 16 个槽)。


三、节点与槽的映射关系

每个 Redis 节点负责一部分连续的(或不连续)哈希槽。例如一个 3 节点的集群可能分布为:

节点 负责的哈希槽范围
Node A 0 -- 5460
Node B 5461 -- 10922
Node C 10923 -- 16383

集群内部维护一个 槽→节点 的映射表,每个节点都知道所有槽分别由谁负责(通过 CLUSTER NODES 命令可查看)。

可视化:槽分布示例

33% 33% 33% 哈希槽分布示例 (3节点) Node A (0-5460) Node B (5461-10922) Node C (10923-16383)


四、数据路由流程(正常情况)

当客户端发送一个命令(如 GET mykey)时,流程如下:
负责该槽的节点 任意节点(入口) 客户端 负责该槽的节点 任意节点(入口) 客户端 1. GET mykey 2. 计算 CRC16("mykey") % 16384 = 5200 3. 查本地槽映射表:槽5200由 NodeB 负责 4. 返回 MOVED 5200 127.0.0.1:6379 5. 重定向到 NodeB 执行 GET mykey 6. 返回结果

注意:如果客户端一开始连接的就是正确节点,则直接返回数据,无 MOVED 重定向。智能客户端(如 JedisCluster)会缓存槽映射,减少重定向次数。


五、故障转移:节点宕机时哈希槽如何处理?

Redis Cluster 的高可用依赖于主从架构:每个主节点负责若干槽,并配有至少一个从节点。当主节点宕机时:



主节点 M1 宕机
集群中超过半数主节点

认为 M1 失联?
从节点 S1 发起选举
获得多数主节点投票?
S1 晋升为新的主节点
S1 接管 M1 原来的所有槽
集群恢复,槽映射更新
集群标记 M1 为 FAIL,

但暂不切换

核心点 :槽是绑定在主节点上的逻辑单元,主节点故障后,其从节点会原封不动地继承那些槽,客户端无需感知槽位的变化(只需获取新的 MOVED 重定向)。

如果某个槽既没有主节点也没有从节点(所有相关节点全挂),则整个集群不可用 ------ 因为一部分数据无法访问。


六、节点扩容/缩容时的槽迁移

增加或删除节点时,哈希槽需要在节点间重新分配 ,并且槽内的 key 也要随之迁移

迁移过程(简化)
源节点导出槽
目标节点导入槽
逐个迁移key
迁移完成
更新集群槽映射

Redis 提供了 CLUSTER SETSLOTCLUSTER GETKEYSINSLOT 等命令以及 redis-cli --cluster rebalance 工具来平滑迁移。迁移过程中,客户端访问正在迁移的槽时,可能会收到 ASK 重定向,与 MOVED 类似但属于临时状态。


七、哈希槽 vs 一致性哈希

对比项 一致性哈希(如 Redis Cluster 前的老方案) Redis 哈希槽
数据分片方式 哈希环 + 虚拟节点 固定 16384 个槽
节点增减影响范围 只影响相邻节点,但数据迁移粒度较粗 精确控制每个槽的迁移
元数据大小 虚拟节点映射表可能较大 16384 长度的 bitmap,极紧凑
实现复杂度 较高(需维护环) 较低,槽映射清晰
客户端路由 需计算哈希并查找环 计算槽位,查表即可

哈希槽的优势:简单、确定性强、迁移粒度可控、元数据极小(每个节点只需知道 2KB 的位图即可描述全部槽的归属)。


八、常见面试题与解答

问:Redis Cluster 中一个 key 到底存在哪个节点?

答:先对 key 做 CRC16 取模得到槽号,再根据集群内部槽→节点映射表找到负责该槽的主节点。

问:为什么哈希槽数量是 16384,而不是 65536 或其他?

答:16384 刚好是 2^14,每个槽用 1 bit 表示归属,16384 bits = 2KB。若用 65536 bits = 8KB,心跳包会更大;且 Redis 节点数量通常不会超过 1000,16384 个槽足够均匀分配。

问:如果客户端缓存了槽映射,但集群发生了故障转移或重新分片,会怎样?

答:客户端按旧映射访问节点时,节点会返回 MOVED 错误,并告知新地址。智能客户端会更新本地缓存,下次直接访问正确节点。

问:能否手动将某个槽从一个节点移到另一个节点?

答:可以。使用 CLUSTER SETSLOT <slot> NODE <node_id> 命令,但需要先迁移槽内的所有 key(否则会丢失数据)。生产环境推荐使用 redis-cli --cluster reshard


九、代码示例:计算 key 的槽位

python 复制代码
# Python 示例:计算任意 key 属于哪个哈希槽
def get_slot(key):
    import binascii
    # 模拟 Redis CRC16 实现(实际 Redis 有自己的 CRC16 查表算法)
    crc = binascii.crc_hqx(key.encode('utf-8'), 0xFFFF)
    return crc % 16384

print(get_slot("user:1001"))   # 输出例如 4210
print(get_slot("order:2024"))  # 输出例如 12987

在 Redis 客户端中,可以直接使用 CLUSTER KEYSLOT <key> 命令查看:

bash 复制代码
> CLUSTER KEYSLOT user:1001
(integer) 4210

十、总结

  • 哈希槽是 Redis Cluster 的数据分片基础 :固定 16384 个槽,通过 CRC16(key) % 16384 映射。
  • 每个槽由唯一一个主节点负责,可以迁移给其他节点(扩容、缩容、故障转移)。
  • 客户端访问流程:计算槽 → 查映射表 → 直接访问负责节点,错误时收到 MOVED/ASK 重定向。
  • 高可用:主节点宕机时,从节点自动继承其槽并升级为主节点。
  • 对比传统分片方案:哈希槽设计更简洁、元数据更小、迁移粒度精细。

如果你正在搭建 Redis 集群,请牢记:槽均匀,数据才均匀;槽映射稳定,集群才稳定。

相关推荐
未秃头的程序猿2 小时前
🚀 从“单机崩盘”到“集群稳如狗”:Redis 高可用避坑指南(保姆级实战)
redis·后端·面试
山甫aa4 小时前
哈希集合-----从零开始的数据结构学习
数据结构·算法·哈希算法
XiYang-DING4 小时前
【Java】哈希
java·哈希算法·散列表
见山是山-见水是水4 小时前
鸿蒙flutter第三方库适配 - 汇率换算器
redis·flutter·华为·harmonyos
014-code4 小时前
Redis 删除缓存失败怎么办?重试、死信、补偿的工程化方案
数据库·redis·缓存
rannn_1114 小时前
【Redis|高级篇1】分布式缓存|持久化(RDB、AOF)、主从集群、哨兵、分片集群
java·redis·分布式·后端·缓存
PD我是你的真爱粉4 小时前
Redis 持久化、过期删除、淘汰策略与内存碎片全解析
java·redis·bootstrap
斌味代码4 小时前
Redis 分库分表实战:从垂直拆分到水平扩容完整记录
数据库·redis·bootstrap
rchmin5 小时前
阿里Tair分布式锁与Redis分布式锁的实现区别
数据库·redis·分布式