首页/数据/NoSQL 速查/一致性哈希与虚拟节点
选型与架构50 条

一致性哈希与虚拟节点

普通取模在扩容时会让多数 key 重新映射;一致性哈希把节点与 key 映射到同一个环上,扩缩容只影响相邻区间。虚拟节点用于缓解分布不均。Redis Cluster 使用 16384 槽位,思路相近但实现不同。

NoSQL 速查数据更新于 2026-10-13

为什么要一致性哈希

分片存储面临同一个问题:节点数变化时,key 与节点的映射要尽量少变。用取模(hash(key) % N)最简单,但 N 从 4 改成 5 会让约八成的 key 换节点,缓存集体失效、数据大规模搬迁。一致性哈希把节点与 key 都映射到同一个整数环上,key 顺时针找到第一个节点;增删一个节点只影响相邻一段区间,其余映射不动。

基本信息

核心思想节点与 key 同环映射,key 归属顺时针第一个节点
虚拟节点每个物理节点在环上放多个副本点(常见 100-200),平衡数据分布
典型实现Redis Cluster 用 16384 个固定槽位,key 经 CRC16 取模定位槽
扩缩容影响普通取模几乎全量重映射;一致性哈希只迁移相邻区间
槽位迁移Redis Cluster 支持槽级搬迁,迁移期间槽内 key 有重定向提示

典型使用场景

  • 自研分片缓存或分片库:节点扩缩容时希望缓存命中率不整体塌陷
  • 多级缓存或本地缓存的路由决策:按 key 固定落到同一进程,提高本地命中率
  • 有状态服务分片:会话、连接与 key 绑定,节点增减时只搬迁部分分片
  • 理解 Redis Cluster 的槽位与重定向机制,排查 MOVED 与 ASK 报错
  • 需要把同一用户的多个 key 固定到同一节点时,用哈希标签替代改哈希函数

环与虚拟节点(简化示例)

环与虚拟节点(简化示例)
import bisect, hashlib

class Ring:
    def __init__(self, nodes, vnodes=100):
        self.points = []          # 环上所有虚拟节点点,按哈希值排序
        for n in nodes:
            for i in range(vnodes):
                h = hashlib.md5(("%s#%d" % (n, i)).encode()).hexdigest()
                self.points.append((int(h, 16), n))
        self.points.sort()

    def node_for(self, key):
        h = int(hashlib.md5(key.encode()).hexdigest(), 16)
        # 顺时针找到第一个虚拟节点,找不到则回到环首
        i = bisect.bisect(self.points, (h,)) % len(self.points)
        return self.points[i][1]

# 虚拟节点数量决定均衡度:节点少时建议 100 以上,# 节点多时可适当减少以节省内存

取模与一致性哈希对照

维度普通取模一致性哈希
节点数变化时迁移量约 (N-1)/N 的 key 需要重映射仅相邻区间迁移,约 1/N 左右
数据分布均衡度哈希均匀时天然均衡不引虚拟节点容易倾斜,靠副本点修正
实现复杂度一行代码需要维护环与节点上下线逻辑
典型使用者分片数固定的简单场景Redis Cluster、Cassandra、自研分片中间件
Redis Cluster 固定 16384 个槽,key 用 CRC16 取模定位槽位;想让同一用户的多个 key 落在同一槽可用哈希标签(形如 user:{42}:name),槽内批量操作与事务才可能命中同一节点。
上线后更换哈希函数会让几乎所有 key 重新映射:迁移需要双读双写或先预热新位置,否则会出现大规模缓存穿透打向后端存储。

易错点

  • 不引入虚拟节点:少量物理节点在环上分布不均,热点集中到个别区间,扩容也解决不了倾斜
  • 虚拟节点数量极少(如每节点 1 个):节点掉线时其区间整体压给下一个节点,负载翻倍
  • 把一致性哈希当成数据不丢的保证:它只负责路由,副本与持久化仍要单独设计
  • 扩容时一次性搬完槽位或改全量哈希:应分批迁移并监控,避免搬迁流量与业务流量叠加压垮集群

数据来源与参考

  • Redis Docs: Cluster specification
  • Cassandra Docs: Consistent hashing(概念参考)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌