⚠️ 一、为什么需要一致性哈希
最朴素的办法是"取模分片":节点 = hash(key) % N。它简单,但有个致命问题——节点数 N 一变,几乎所有数据都要重新映射。
取模法的灾难:3 台机器扩到 4 台,
hash(key) % 3 变成 hash(key) % 4。理论上有 ≈ 75% 的数据会落到不同的节点上,引发大规模数据迁移,缓存系统瞬间击穿、数据库被打爆。节点频繁上下线的分布式环境根本扛不住。一致性哈希要解决的核心矛盾:既要把数据均匀分散到多节点,又要让节点增减时,只影响极小一部分数据,而不是全量重分布。
🧩 二、核心原理:把"地址空间"排成一个环
一致性哈希把哈希值空间(如 0 ~ 2³²-1)首尾相连,想象成一个顺时针的环。节点和数据都通过同一个哈希函数映射到环上。
构建哈希环
取一个哈希函数(如 MD5、SHA,或 MurmurHash),把结果对 2³² 取模,得到一个 [0, 2³²) 的整数区间,首尾相接成环。
节点上环
对每个节点(用 IP、名字等)做哈希,得到它在环上的位置。例如 Node-A、Node-B、Node-C 各占环上一段。
数据上环
对数据 key 同样做哈希,得到环上某点。沿环顺时针走,遇到的第一个节点就是它归属的节点。
增减节点只动相邻数据
新增节点只"抢走"环上它到前一个节点之间的数据;删除节点只把它的数据顺时针交给下一个节点。其余节点的数据纹丝不动。
⭕ 三、哈希环图解
下图中,3 个节点把环分成 3 段;数据 K1~K5 顺时针找最近节点。注意:顺时针是约定方向。
K1 顺时针最近的节点是 A;K2/K3 落到 B;K4 落到 C;K5 顺时针越过顶部回到 A。当新增节点 D 插在 A 与 C 之间(环左下方),只有原本属于 A 的一部分数据改投 D,B、C 完全不受影响。
🎭 四、虚拟节点:解决"数据倾斜"
裸的一致性哈希有个问题:节点少时,它们在环上的分布可能极不均匀——某台机器"占了一大半环",数据严重倾斜。解决方法是虚拟节点(vnode)。
虚拟节点做法:每台物理节点复制成多个"虚拟节点"(如
Node-A#1, Node-A#2 ... Node-A#100),统统打散到环上。数据先落到虚拟节点,再映射回真实节点。虚拟节点越多,数据分布越均匀(实践中常用 100~200 个)。无虚拟节点
3 个真实节点 → 环上 3 点,倾斜明显;某节点宕机,其负载全压给顺时针下一台,雪崩。
有虚拟节点
每台 100 个 vnode → 环上 300 点,均匀分布;单节点宕机,它的 vnode 平均分散给其余所有节点,影响被摊薄。
📊 五、三种分片寻址方案对比
| 方案 | 节点增减的数据迁移量 | 数据均匀度 | 典型应用 |
|---|---|---|---|
取模 hash % N | 几乎全量(≈ (N-1)/N) | 均匀 | 固定分片、不可扩缩场景 |
| 一致性哈希(无 vnode) | 仅相邻段(小) | 易倾斜 | 早期 Memcached 客户端 |
| 一致性哈希 + 虚拟节点 | 仅相邻段(小) | 均匀 | Redis Cluster、Cassandra、DynamoDB |
一句话记忆:一致性哈希 = 哈希环 + 顺时针归属 + 虚拟节点。它用"局部重映射"换来了"弹性扩缩容"。
🎯 六、面试高频追问
一致性哈希为什么能减少数据迁移?
因为只有"新节点到前一个节点之间"的弧段数据会改变归属,其余弧段映射不变。迁移量从 O(N) 降到 O(1/N)。
为什么需要虚拟节点?
真实节点少时环上分布不均、易倾斜;且单节点故障会把全部压力给下一台。虚拟节点把负载打散、摊薄,提升均匀度与容错。
Redis Cluster 用一致性哈希吗?
不是直接用。Redis Cluster 用 16384 个固定 slot,key 经 CRC16 映射到 slot,slot 再分配给节点。本质是把"环"离散成有限槽位,效果等价于一致性哈希(扩缩容只迁移相关 slot)。
和"范围分片"怎么选?
哈希分片(一致性哈希)利于均匀分布与扩缩容,但范围查询弱;范围分片(按时间/ID 区间)利于区间扫描,但易热点。按访问模式选型。
延伸阅读:数据分片与分区策略 · 数据复制(主从/多主/无主) · 返回分布式系统总目录