0x9a.com

保持简单,保持好奇

分布式分片算法:一致性哈希与 Hash Slot

2026-03-17

如果你玩过早年的网游,大概率都见过这样的界面:

这其实就是最朴素的“分布式”雏形:按服务器切分用户与数据。它简单有效,但也留下了长期代价。

这篇文章想回答两个问题:

  1. 为什么系统会从“分服”走向更通用的分布式分片?
  2. 在分片算法上,为什么 Redis 选择 Hash Slot,而不是纯一致性哈希环?

1. 为什么要做分布式:从分服到统一系统

1.1 分服时代为什么可行

“分服”本质上是业务级分片:

在业务早期,这种方式实现成本低、组织上也好管理。

1.2 分服模式的长期代价

当业务发展到中后期,问题会逐渐显现:

1.3 走向分布式后的核心目标

现代分布式系统通常希望做到:

而这些目标最终会落到一个非常底层的问题:

f(key, topology) -> node

也就是:给定 key 和当前拓扑,如何稳定地找到目标节点。


2. 起点:hash(key) % N

最朴素的分片算法:

node = hash(key) % N

优点是简单且常数开销低;问题是 单调性差(topology 一变,映射大面积变化)。

N 扩到 N+1 时,可近似得到:

例如 4 节点扩到 5 节点,约 80% key 会重映射。这在缓存与在线业务场景里通常不可接受。

3. 一致性哈希:优化“扩缩容扰动”

一致性哈希把节点和 key 都映射到哈希环上,key 顺时针归属到第一个节点。

它解决的核心是:

为了降低倾斜,工程上常加虚拟节点(vnodes),把单节点映射成多个点位,以改善负载方差。

从算法角度看,一致性哈希的优势非常明确:

4. 一致性哈希的代价:控制面复杂度上升

一致性哈希并非“无代价最优”。在大规模系统里常见三类成本:

  1. 负载平衡依赖参数
  2. vnode 数量、权重策略会影响倾斜程度;
  3. 参数不当时,热点和不均衡仍会出现。

  4. 路由状态更复杂

  5. 客户端往往要维护 ring 元数据(含 vnode);
  6. 拓扑频繁变化时,状态收敛与一致性管理更难。

  7. 重平衡可控性不够离散

  8. 你能迁移局部区间,但很难像“搬 200 个固定分区”那样显式操作。

所以问题从“迁移是否足够少”,变成了“迁移是否足够可控”。

5. Redis Hash Slot:把连续空间离散化

Redis Cluster 使用两级映射:

key --(CRC16 % 16384)--> slot --(slot owner)--> node

这相当于把连续哈希空间先离散为固定 16384 个桶(slot),再把桶分配给节点。

算法上,它保留了一致性哈希“局部迁移”的思想,同时把控制面变成了离散操作:

6. 为什么 Redis 选择 Slot 而不是纯 Ring

6.1 迁移粒度可计算、可编排

如果迁移 K 个 slot,则受影响 key 的期望比例约为:

K / 16384

这让迁移计划天然可量化:

6.2 路由状态规模固定

slot 总数固定,路由状态上界稳定。无论 key 规模多大,控制面复杂度主要受节点数和 slot 分配影响,而不是受 ring 参数直接驱动。

6.3 多 key 共址有显式机制

Redis 的 hash tag(如 user:{42}:profileuser:{42}:cart)可强制 key 进入同一 slot。

这在事务、Lua、多 key 操作中非常关键:共址从“概率事件”变成“建模能力”。

6.4 故障切换语义更直接

主从切换时,本质是 slot 归属转移。控制语义清晰,恢复路径明确。

7. 为什么是 16384(2^14)

这是典型工程折中:

槽位太少,迁移颗粒粗;槽位太多,控制面开销上升。16384 是一个长期实践后的平衡点。

8. Redis Cluster 的实际运行细节

上面讲的是算法与控制面思路。真正在线上稳定运行,还依赖一套运行时机制。

8.1 客户端路由:MOVED 与 ASK

Redis Cluster 的客户端通常会缓存 slot -> node 路由表,但这张表不是永远不变。

当客户端发到“错误节点”时,服务端会返回重定向:

可以把它理解为:

8.2 槽位迁移:导入/导出状态与双端协作

slot 从 A 迁到 B 时,不是瞬间切换,而是一个过程:

  1. 配置 slot 在源节点为迁出(migrating),目标节点为迁入(importing)
  2. 将该 slot 下的 key 分批从 A 搬到 B
  3. 客户端请求期间可能收到 ASK 重定向
  4. 全量搬完后,更新 slot 最终归属,后续走 MOVED/新路由

这个流程的价值在于:

8.3 故障检测与转移:谁来接管 slot

Redis Cluster 通过节点间 Gossip 交换状态,识别疑似下线(PFAIL)和客观下线(FAIL)。

当某主节点被确认失败后,其从节点会参与提升流程。提升成功后,本质动作是:

从分片语义看,故障转移不是“某台机器挂了”这么简单,而是“slot 所有权发生了重绑定”。

8.4 多 key 约束:为什么要 Hash Tag

在 Cluster 模式下,很多多 key 命令要求 key 位于同一 slot。否则会出现跨槽错误(常见为 CROSSSLOT)。

所以业务建模时会主动使用 hash tag:

同一 {1001} 保证同槽,才能稳定支持事务、Lua 或批量操作。

8.5 热点与再均衡:算法之外的运营问题

即使 slot 分布均匀,也可能出现业务热点(某些 key 访问远高于平均值)。

实践上通常需要组合手段:

这说明分片算法解决的是“结构性均衡”,而不是“业务流量永远均衡”。

9. 三种算法的核心对比

方案 扩容重映射 负载均衡 控制面复杂度 可控迁移粒度
hash % N 高(约 N/(N+1)
一致性哈希 低(局部) 中-高(依赖 vnode) 中-高
Hash Slot 低(按 slot) 高(可调 slot 分配) 中(固定上界)

Redis 的选择不是否定一致性哈希,而是把“低扰动映射”进一步工程化为“离散、可编排、可观测”的分片算法。

结语

从“分服”到“分布式”,再到具体的分片算法,演进主线很清晰:

从“先把流量拆开”,走向“在可治理前提下持续扩展”。

%N、一致性哈希、Hash Slot 并不是互相取代的教条关系,而是不同阶段对“复杂度预算”的取舍。

在分布式工程里,数学性质决定上限,而控制面的可操作性决定系统能跑多久。

← 返回首页