一篇文章带你看懂一致性hash
一致性Hash是分布式系统中的核心算法,主要用于解决服务器扩容时的数据迁移问题。与传统Hash取模方式不同,它将数据和服务节点映射到一个环形哈希空间,通过顺时针查找确定数据归属。当节点增减时,仅需迁移相邻区间的数据,大幅减少数据迁移量。虚拟节点技术的引入进一步解决了数据倾斜问题,使分布更均匀。虽然现代系统如Redis Cluster已采用Hash Slot方案,但一致性Hash思想仍广泛应用于分布
一致性 Hash 是什么?为什么它是分布式系统中的核心算法
很多程序员第一次接触“一致性 Hash”,都会有一种感觉:
这个名字听起来很高级。
但真正去看资料时,却发现各种概念扑面而来:
- Hash Ring(哈希环)
- 虚拟节点
- 顺时针查找
- 数据迁移
- 分布式缓存
最后越看越晕。
我第一次学习一致性 Hash 的时候,也是这种感觉。
直到后来真正做了分布式系统、Redis 集群、分库分表之后,才真正理解:
一致性 Hash 本质上其实是在解决一个非常现实的问题。
也就是:
当服务器数量变化时,如何尽量减少数据迁移。
今天这篇文章,我会尽量用“小白也能看懂”的方式,把一致性 Hash 讲清楚。
包括:
- 为什么会有一致性 Hash
- 普通 Hash 的问题是什么
- 一致性 Hash 如何解决问题
- 什么是 Hash 环
- 什么是虚拟节点
- Redis 为什么后来不用一致性 Hash
- 工程里真正的使用场景
在进入主题之前,可以先直观的看下一致性hash的原理视频:
5月18日
更多精彩的算法动画,可以访问:算法动图
一、从一个最简单的问题开始
假设现在有 3 台服务器:
ServerA
ServerB
ServerC
现在我们需要存储用户数据:
user_1
user_2
user_3
...
那么问题来了:
user_1 应该放到哪台服务器?
最简单的方法是什么?
很多人第一反应就是:
hash(userId) % 服务器数量
例如:
hash(user_1) % 3 = 0 -> ServerA
hash(user_2) % 3 = 1 -> ServerB
hash(user_3) % 3 = 2 -> ServerC
这就是最经典的:
普通 Hash 分片
它的优点非常明显:
- 简单
- 快
- 数据比较均匀
很多系统最开始都是这么做的。
二、普通 Hash 最大的问题
问题出现在:
扩容
假设原来:
3 台服务器
后来业务量越来越大。
你发现:
ServerA 快扛不住了。
于是决定:
新增一台机器
变成:
4 台服务器
那么原来的计算规则:
hash(key) % 3
就会变成:
hash(key) % 4
这时候会发生什么?
举个例子。
原来:
hash(user_1) % 3 = 1
可能在:
ServerB
现在:
hash(user_1) % 4 = 2
突然变成:
ServerC
也就是说:
几乎所有数据的位置都变了
这就是普通 Hash 最大的问题。
三、为什么这是灾难?
很多小白第一次看到这里,会觉得:
“位置变了就变了呗,有什么问题?”
实际上问题非常严重。
因为:
数据需要迁移
原来在:
ServerB
现在应该去:
ServerC
那你就得:
- 把数据重新搬迁
- 重新同步
- 重新缓存
如果系统数据量非常大:
几百 GB
几 TB
甚至 PB
那么扩容一次,系统可能直接崩掉。
尤其是:
分布式缓存
比如:
- Redis
- Memcached
如果大量 Key 失效:
会出现:
缓存雪崩
数据库压力瞬间暴增。
于是大家开始思考:
有没有一种算法,可以在扩容时,尽量少迁移数据?
这就是:
一致性 Hash 出现的背景
四、一致性 Hash 的核心思想
一致性 Hash 最核心的思想只有一句话:
不再对机器数量取模。
而是:
把整个 Hash 空间组织成一个环。
五、什么是 Hash 环?
假设 Hash 的范围是:
0 ~ 2^32-1
正常情况下它是一条线:
0 ------------------> 2^32-1
一致性 Hash 会把它首尾连接起来:
0 -----------> 2^32-1
^ |
|_______________|
于是:
它变成了一个圆环
这就是:
Hash Ring(哈希环)
六、服务器如何放到环上?
现在我们不再用:
% 服务器数量
而是:
对服务器本身做 Hash
例如:
hash(ServerA) = 100
hash(ServerB) = 500
hash(ServerC) = 800
于是:
ServerA -> 环上的 100
ServerB -> 环上的 500
ServerC -> 环上的 800
整个 Hash 环变成:
0 ----100(A)----500(B)----800(C)----0
七、数据如何找到服务器?
现在:
假设:
hash(user_1) = 320
那么它属于谁?
一致性 Hash 的规则是:
顺时针找到第一个服务器
例如:
320 -> 顺时针找到 500(B)
于是:
user_1 -> ServerB
再比如:
hash(user_2) = 750
顺时针找到:
800(C)
于是:
user_2 -> ServerC
八、一致性 Hash 为什么能减少数据迁移?
重点来了。
假设现在新增一台服务器:
ServerD
Hash 后:
hash(ServerD) = 650
那么环会变成:
0 ----100(A)----500(B)----650(D)----800(C)----0
注意:
只有:
500 ~ 650
这部分数据会受到影响。
也就是说:
只有局部数据需要迁移
其他数据完全不动。
这就是一致性 Hash 最核心的价值。
九、普通 Hash 和一致性 Hash 的区别
普通 Hash:
机器数量变化
↓
全部重新计算
↓
大量数据迁移
一致性 Hash:
机器变化
↓
只影响相邻区间
↓
局部数据迁移
这就是为什么它叫:
一致性 Hash
因为它在节点变化时,整体映射关系仍然相对“稳定”。
十、但一致性 Hash 还有一个问题
看到这里很多人会觉得:
“这算法已经很完美了。”
但实际上:
它还有一个非常严重的问题。
数据倾斜
举个例子。
假设:
ServerA -> 100
ServerB -> 110
ServerC -> 900
环:
100(A)---110(B)--------------------900(C)
那么:
110 ~ 900
这一大段数据:
全部归 ServerC。
于是:
- ServerC 压力巨大
- A/B 很闲
- 数据严重不均衡
十一、虚拟节点是怎么解决问题的?
这是很多人第一次学一致性 Hash 时最懵的地方。
其实虚拟节点非常简单。
以前:
一台服务器:
ServerA
只映射一次。
现在:
一台服务器映射很多次
例如:
ServerA#1
ServerA#2
ServerA#3
ServerA#4
它们本质上都代表:
ServerA
只是:
在 Hash 环上有多个位置
例如:
A#1 -> 100
A#2 -> 400
A#3 -> 700
A#4 -> 950
于是:
ServerA 就不再只负责一个大区间。
而是负责:
多个小区间
最终:
数据会更加均匀
十二、为什么虚拟节点效果这么好?
因为:
随机越多,越均匀
如果:
每台机器只有 1 个节点。
那么:
可能运气不好
导致分布极不均匀。
但如果:
每台机器有:
100 个虚拟节点
那么:
大数定律会让整体趋于平均
这也是为什么:
很多工程系统都会使用:
虚拟节点
十三、Redis 为什么后来不用一致性 Hash?
很多人学完一致性 Hash 后,会有一个疑问:
Redis Cluster 为什么不用一致性 Hash?
实际上:
Redis Cluster 使用的是:
Hash Slot(槽位)
Redis 固定定义:
16384 个槽
例如:
slot = CRC16(key) % 16384
然后:
把 Slot 分配给机器
这样做的好处是:
- 更容易迁移
- 更容易统计
- 更容易运维
- 更容易做主从复制
所以:
现代很多分布式系统,其实已经不直接使用一致性 Hash 了。
而是:
Slot 思想
但一致性 Hash 的思想依旧非常重要。
十四、一致性 Hash 的真实应用场景
它主要用于:
1、分布式缓存
例如:
- Redis
- Memcached
客户端根据一致性 Hash 找缓存节点。
2、分库分表
例如:
128 个库
每库 32 张表
很多系统会用一致性 Hash 做路由。
3、CDN
请求:
图片
视频
静态资源
根据一致性 Hash 找缓存节点。
4、对象存储
例如:
- Cassandra
- Ceph
十五、一句话理解一致性 Hash
如果让我只用一句话总结:
普通 Hash 解决“怎么分配数据”
一致性 Hash 解决“如何平滑扩容”
十六、最后
很多人第一次学习分布式系统时,会觉得:
一致性 Hash 很复杂。
但当你真正理解它之后,你会发现:
它本质上是在解决一个非常工程化的问题:
如何让系统在不停机的情况下,动态扩容。
而这,恰恰是现代互联网系统最核心的能力之一。
很多算法的伟大之处,并不在于数学有多复杂。
而在于:
它真正解决了现实世界的问题。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)