什么是一致性哈希?
一、为什么需要一致性哈希算法?
假设我们有三台缓存服务器分别为:node0、node1、node2,有3000万个缓存数据需要存储在这三台服务器组成的集群中,希望可以将这些数据均匀的缓存到三台机器上,该如何设计方案呢?
首先方案是:取模算法hash(key)%N
,即:对缓存数据的key进行hash运算后取模,N是机器的数量;运算后的结果映射对应集群中的节点。具体如下图所示:
但是,在分布式集群系统的负载均衡实现上,这种模型**在集群扩容和收缩时却有一定的局限性**。因为当系统的机器数量N发生变化时,会导致整个集群的缓存数据需要重新计算调整,从而造成大量缓存同时失效,即缓存雪崩。为了解决这个问题,因此提出了一致性哈希算法。
二、一致性哈希原理
2.1 算法介绍
一致性哈希算法本质上也是一种取模算法。只不过前面介绍的取模算法是按服务器数量取模,而**一致性哈希算法是对固定值2^32取模,这就使得一致性算法具备良好的单调性:不管集群中有多少个节点,只要key值固定,那所请求的服务器节点也同样是固定的。**
具体来说:
- 一致性哈希算法将整个哈希值空间映射成一个圆环,取值范围为0~ 2 32 2^{32} 232 -1;
- 计算各个节点的哈希值,将服务器映射到哈希环上;
- 将数据请求的key值进行哈希运算计算出对应的哈希值,映射到哈希环上,然后**沿着哈希环顺时针查找,遇到的第一个服务器就是对应的处理请求服务器**;
- 当增加或删除一台服务器时,受到影响的仅仅是**该服务器和环空间中前一台服务器之间的数据**,其他不会影响。
一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性 。
2.2 服务器扩容&缩容
2.2.1 服务器扩容
服务器扩容就是集群中需要增加一个新的数据节点,此时,只需要计算新节点的哈希值并将新的节点加入到哈希环中,然后将哈希环中**从上一个节点到新节点之间的数据映射到新的数据节点**即可。
2.2.2 服务器缩容
服务器缩容就是减少集群中服务器节点的数量或是集群中某个节点故障。本映射到该节点的请求,会找到哈希环中的下一个节点,数据也同样被重新分配至下一个节点,其它节点的数据和请求不受任何影响。这样就确保节点发生故障时,集群能保持正常稳定。
2.3 数据倾斜与虚拟节点机制
2.3.1 什么是数据倾斜?
由于哈希计算的随机性,导致一致性哈希算法存在一个致命问题:数据倾斜,即大多数访问请求都会集中少量几个节点的情况(特别当节点太少时)。
如上图所示,key-1、key-2、key-3可能被映射到同一个节点node0上。导致node0负载过大,而node1和node2却很空闲的情况。这有可能导致个别服务器数据和请求压力过大和崩溃,进而引起集群的崩溃。
2.3.2 怎么解决数据倾斜?
一致性哈希算法引入了虚拟节点机制,即对**每一个物理服务节点映射多个虚拟节点,将这些虚拟节点计算哈希值并映射到哈希环上,当请求找到某个虚拟节点后,将被重新映射到具体的物理节点**。虚拟节点越多,哈希环上的节点就越多,数据分布就越均匀,从而避免了数据倾斜的问题。
三、总结
一致性哈希(Consistent Hash)算法是1997年提出,是一种特殊的哈希算法,目的是解决分布式系统的数据分区问题:当分布式集群移除或者添加一个服务器时,必须**尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系**。
一致性哈希算法是分布式系统中的重要算法,使用场景也非常广泛。主要是是负载均衡、缓存数据分区等场景。常见的使用示例:
- RPC框架Dubbo用来选择服务提供者
- 分布式关系数据库分库分表:数据与节点的映射关系
- LVS负载均衡调度器