假设有N台redis作为缓存,传统的Hash算法是:ser = hash(k) % N
,即对键值哈希,然后对服务器的个数取余,这样做简单、而且分布均匀,但是有两个问题:
- 假设有服务器x
发生了宕机,那么此时取余命中x
是失效的,我们需要对键值进行相应的迁移,把服务器变成N-1台,但是这样面临服务器一定时间不可用的情况,造成redis雪崩
- 假设容量不够,我们需要扩容,此时新加入服务器后,也需要改变为N + 1
,同样有迁移key造成雪崩的问题。
综上可知,取余的哈希的核心缺陷在于:服务器变动后,会影响N,造成整体key的迁移
N
变化是造成整体不可用的核心原因,最容易想到的方式是:
让
,然后对每个服务的IP或者其它的值进行哈希取余,使这些数据分布在0-N的一个圆环上。之后对key
取余,然后顺时针的对应到相应的服务器上,如下图:
在哈希环上的值,按照顺时针方向,去寻找自己的服务器,这样就可以找到对应的服务了。
为什么一致性哈希可以解决传统方式的雪崩问题:
假设下面一个场景,服务器C崩溃,此时请求C的哈希值,会落在D上,而请求A、B、D的服务不会发生变化,制造成了1/4的不可用。同样的,假设加入服务器X在A和B之间,此时请求B的服务,会有一部分去X上,也是影响了部分的流量。通过一致性哈希的方式,把影响降到了最小。参考下面两个图:
一致性哈希相对于传统哈希来说,最大的缺陷在于服务数量少的时候,可能造成请求不均衡的情况,比如下面这个图片:
大量的数据都去了节点A,只有少部分数据在节点B上。
缓解方案: