维基百科给出的定义: 「 在使用一致性哈希算法后,哈希槽位数(大小)的改变平均只需要对 K/n 个关键字重新映射,其中 K 是关键字的数量,n 是槽位数。」
在传统的哈希表中,添加或删除一个槽位需要对所有关键字进行重新映射。
一致性 hash 算法提出了在动态变化的 cache 环境中,判定哈希算法好坏的四个定义:
假设一个简单的场景:有四个 node ( 服务器)组成的集群,当一个对象 object 传入集群使,这个对象应该存储在哪个 node 呢?
一致性哈希将整个哈希值空间组织成一个虚拟的圆环,假设某个哈希函数 H 的值的空间为0 - 2^32-1,整个哈希空间如下图(整个空间按照顺时针方向组织,0和2^32-1在零点方式重合):
)
对 node 集群所有节点,使用哈希函数计算其在 hash 环中的位置。可以选择服务器的 ip 活主机名作为关键字进行哈希,以确定其在哈希环上的位置,假设对 node 服务器使用 ip 地址哈希后在环空间的位置如下图所示:
将 object 使用相同的哈希函数计算出哈希值,并确定该 object 在此数据环上的位置,从此位置沿环顺时针「行走」,第一个遇到的 node 服务器就是应该定位的服务器。
例如 Object A、B、C、D 四个数据对象,经过哈希计算后,在环空间上的位置如下图:
根据一致性哈希算法, Object A、B、C、D分别回映射到服务器集群的node 1、2、3、4 上。
假设 node 3 不幸宕机,可以看到此时 object A、B、D 不会受到影响,只有 C 对象被重新定位到 node 2。一般的,在一致性哈希算法中,如果一台服务器不可用,受影响的数据仅仅是原本映射到该服务器上的数据,其它不会受到影响。
此时对象 Object A、B、D 不受影响,只有对象 C 需要重新定位到新的 node X。一般的,在一致性算法中,如果增加一台服务器,受影响的数据仅仅是新服务器到其闭环中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器) 之间的数据,其它数据不会受到影响。
综上所述,一致性哈希算法对于节点的增减都只需要重定位环空间中的小部分数据,具有较好的容错性和扩展性。
有一种情况是:将 node 进行哈希后,这些值没有均匀地落在环上,最终会导致,这些节点所管辖的范围不均匀,最终导致了数据分布的不均匀:
注:一致性哈希算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜问题。
此时必然会导致大量的数据会映射到 node 1上,而只有极少量的数据会定位到 node 2。为了解决这种数据倾斜的问题,一致性哈希引入了虚拟节点机制,使得每个节点在环上所「管辖」更均匀。这样就能保证在节点变化时,尽可能小的影响数据分布的变化,而同时又保证数据分布的均匀。
注:同样数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,就解决了服务节点少数据倾斜的问题。
笔者使用的语言是 golang,此处介绍一种 golang 实现的一致性哈希算法。