一致性哈希

问题背景

假设有N台redis作为缓存,传统的Hash算法是:ser = hash(k) % N,即对键值哈希,然后对服务器的个数取余,这样做简单、而且分布均匀,但是有两个问题:
- 假设有服务器x发生了宕机,那么此时取余命中x是失效的,我们需要对键值进行相应的迁移,把服务器变成N-1台,但是这样面临服务器一定时间不可用的情况,造成redis雪崩
- 假设容量不够,我们需要扩容,此时新加入服务器后,也需要改变为N + 1,同样有迁移key造成雪崩的问题。

综上可知,取余的哈希的核心缺陷在于:服务器变动后,会影响N,造成整体key的迁移

解决方案

N变化是造成整体不可用的核心原因,最容易想到的方式是:

  • 固定住N,不会因为几个服务器的变动影响整个集群
  • 发生服务器变动时,使得key迁移的变动尽可能小

N = 2 32 1 N=2^{32}-1 ,然后对每个服务的IP或者其它的值进行哈希取余,使这些数据分布在0-N的一个圆环上。之后对key取余,然后顺时针的对应到相应的服务器上,如下图:

在哈希环上的值,按照顺时针方向,去寻找自己的服务器,这样就可以找到对应的服务了。

为什么一致性哈希可以解决传统方式的雪崩问题:
假设下面一个场景,服务器C崩溃,此时请求C的哈希值,会落在D上,而请求A、B、D的服务不会发生变化,制造成了1/4的不可用。同样的,假设加入服务器X在A和B之间,此时请求B的服务,会有一部分去X上,也是影响了部分的流量。通过一致性哈希的方式,把影响降到了最小。参考下面两个图:

一致性哈希的不足与缓解方案

一致性哈希相对于传统哈希来说,最大的缺陷在于服务数量少的时候,可能造成请求不均衡的情况,比如下面这个图片:

大量的数据都去了节点A,只有少部分数据在节点B上。

缓解方案:

  • 修正服务的映射方式,使得服务器在哈希环上尽可能均匀分布
  • 使用虚拟节点,增加二级映射的方式。虚拟机节点缓解分布的问题,手动映射虚拟节点到实际节点。如下图: