服务器扩容时,如何避免数据“全员大搬家”?

在分布式缓存(如 Redis Cluster, Memcached)或者负载均衡(Nginx)中,我们经常要把海量的数据(Key)均匀地分配给多台机器(Node)。
最简单的办法是 取模运算 (Hash(key) % N)。
比如你有 3 台机器,Key=10,10 % 3 = 1,那就去 1 号机器。
但是,这个算法有一个致命的缺陷:
如果你想加一台机器(从 3 台变成 4 台),分母变了,所有余数都变了。
这意味着:几乎所有的数据都要换位置。
这会导致缓存雪崩——所有缓存失效,流量瞬间击穿数据库。
为了解决这个问题,一致性哈希 诞生了。它把直线变成了圆圈。
🎡 一、技术分析:把直线弯成圆
1. 哈希环 (Hash Ring)
想象一条无限长的直线,我们把它首尾相接,变成一个圆环。
这个圆环上有 个点(从 0 到 )。
2. 落点规则
- 放机器: 把 3 台服务器(A, B, C)通过哈希算法,算出它们在圆环上的位置。
- 放数据: 把数据(Key)也算出哈希值,落在圆环上。
- 找家: 数据的归属规则是:顺时针往下走,遇到的第一台服务器,就是我的家。
🏠 二、故事场景:服务器的“抢椅子”游戏
为了搞懂它为什么能抗扩容,我们将 服务器 比作 玩游戏的圆桌。
1. 初始状态 (3 台机器)
- 圆桌: 巨大的圆桌边坐着 3 个人:A、B、C。
- 规则: 所有的盘子(数据)被放在桌上。盘子属于它顺时针方向的第一个人。
- 盘子 1 在 A 和 B 之间 -> 归 B 管。
- 盘子 2 在 B 和 C 之间 -> 归 C 管。
- 盘子 3 在 C 和 A 之间 -> 归 A 管。
2. 扩容危机 (加了 1 台机器 D)
-
场景: 业务太火了,你要加一台服务器 D。
-
落座: D 坐在了 B 和 C 之间的位置。
-
影响:
-
只有 D 和 B 之间 的那些盘子,原本是归 C 管的,现在顺时针先遇到了 D,所以要搬家给 D。
-
关键点: 其他地方的盘子(A 的盘子、B 的盘子、C 剩下的大部分盘子)完全不用动!
-
结论: 加一台机器,只影响这一小段数据。数据迁移量极小。
3. 宕机危机 (机器 B 挂了)
- 场景: 服务器 B 突然冒烟了。
- 影响:
- B 走了。原本归 B 管的那些盘子,顺时针往下找,这就找到了 C(或者 D)。
- 这些数据会自动转移给 C。
- 关键点: A 和其他人的数据依然不动。
🥷 三、进阶:数据倾斜与“影分身”术
一致性哈希看似完美,但有个 Bug。
如果机器很少(比如只有 2 台 A 和 B),而且它们凑巧坐得很近。
- 结果: 圆环上一大半的空间都归 A 管,B 只管一小条。A 会被累死,B 在摸鱼。 这叫 数据倾斜 (Data Skew)。
怎么解决?虚拟节点 (Virtual Nodes)。
-
影分身: 既然实体机器少,我就变出很多分身。
-
机器 A 变出 A1, A2, … A100。
-
机器 B 变出 B1, B2, … B100。
-
均匀分布: 把这 200 个分身随机打散在圆环上。
-
结果: 无论真实的 A 和 B 怎么坐,它们的分身都均匀地占满了整个圆环。数据自然也就均匀了。
🎯 四、总结:不折腾才是硬道理
一致性哈希的哲学是:拥抱变化,但拒绝剧变。
- 单调性: 增加机器,旧数据要么不动,要么去新机器,绝不会乱窜到其他旧机器。
- 平衡性: 利用虚拟节点,保证数据均匀分布。









