高 进阶
一致性哈希#
一句话答案#
将节点映射到哈希环,key 顺时针找最近节点,增删节点只影响相邻数据;虚拟节点解决数据倾斜。
核心要点
传统哈希取模的问题:
hash(key) % N(N = 节点数量)
当节点数量变化时(新增或下线一台机器):
N 变了 → 几乎所有 key 的映射都变了 → 大量缓存失效 → 缓存雪崩
例如:3 台变 4 台,约 75% 的 key 需要重新映射plaintext一致性哈希(Consistent Hashing)原理:
① 构建哈希环(Hash Ring):
将哈希值空间组织成一个 0 ~ 2^32-1 的环
② 节点映射到环上:
对每个节点的 IP/名称进行哈希,映射到环上的某个位置
③ Key 映射规则:
对 key 进行哈希 → 在环上顺时针找到第一个节点 → 该节点负责存储这个 key
示意图:
0
│
Node C ─●─── Key1 → 顺时针找到 Node A
│
2^32*3/4 ─────┼───── 2^32*1/4
│
Key3 ──● │ ●── Node A
│
│
Node B ─●─── Key2 → 顺时针找到 Node B
│
2^32/2
新增/删除节点时:
只影响该节点在环上相邻的一小段区间的 key 映射
大部分 key 的映射不变 → 最小化缓存失效plaintext为什么需要虚拟节点?
问题:节点数量少时,哈希环上节点分布不均匀 → 数据倾斜
示例(3 个节点,分布不均):
Node A 负责 60% 的环空间
Node B 负责 30% 的环空间
Node C 负责 10% 的环空间
→ 数据严重倾斜,Node A 压力巨大
解决方案——虚拟节点(Virtual Node):
每个物理节点映射为多个虚拟节点(如 150~200 个)
Node A → VN_A_1, VN_A_2, VN_A_3, ..., VN_A_150
Node B → VN_B_1, VN_B_2, VN_B_3, ..., VN_B_150
Node C → VN_C_1, VN_C_2, VN_C_3, ..., VN_C_150
虚拟节点均匀分散在哈希环上 → 各物理节点负责的数据量趋于均匀
效果:
虚拟节点数量越多 → 数据分布越均匀
一般 150~200 个虚拟节点/物理节点就能达到较好的均匀度plaintext一致性哈希的应用场景:
| 场景 | 说明 |
|---|---|
| 缓存集群分片 | Redis Cluster 之前的客户端分片(如 Jedis ShardedPool) |
| 负载均衡 | Nginx upstream 的 consistent_hash 模块 |
| 分布式存储 | Cassandra、Amazon Dynamo 的数据分片 |
| CDN 节点选择 | 根据内容 URL 哈希选择缓存节点 |
面试回答(2分钟版)
一致性哈希是为了解决传统哈希取模在节点增删时大量数据迁移的问题。传统方式用 hash(key) % N,节点数 N 一变几乎所有 key 的映射都变,导致缓存雪崩。一致性哈希把哈希值空间组织成一个 0 到 2^32 的环,节点和 key 都映射到环上,key 沿顺时针方向找到的第一个节点就是它的归属节点。这样新增或删除一个节点,只影响环上相邻一小段区间的数据映射,大部分 key 不受影响。但节点数量少的时候,环上节点分布不均匀会导致数据倾斜,某些节点负载过重。解决方案是引入虚拟节点,每个物理节点映射出 150 到 200 个虚拟节点均匀分散在环上,这样数据分布就趋于均匀了。实际应用很广泛,比如 Redis Cluster 之前的客户端分片、Nginx 的 consistent_hash 模块、Cassandra 的数据分片以及 CDN 节点选择等场景都用到了一致性哈希。
追问与易错
追问方向:
- “虚拟节点数量设多少?”→ 一般 150~200 个虚拟节点/物理节点就能达到较好的均匀度,数量越多越均匀但内存开销越大,需要权衡
- “怎么处理节点负载不均?”→ 增加虚拟节点数量使数据分布均匀 + 动态调整虚拟节点数量(高性能节点多分配虚拟节点)+ 监控各节点负载及时迁移热点数据
- “Redis Cluster 为什么用哈希槽?”→ 哈希槽(16384 个固定槽位)比一致性哈希更简单直观,槽与节点的映射关系通过 Gossip 协议同步,扩缩容时只需迁移部分槽位
易错点:
- ❌ 一致性哈希完全均匀——没有虚拟节点时可能倾斜
- ❌ 混淆一致性哈希和哈希槽