一致性hash算法
背景
当我们的业务系统大到一定程度的时候,一台缓存服务器显然不能满足需求,需要使用多台缓存服务器。然后缓存服务器具体一定的用户粘性属性,如何设计缓存服务器使其命中率提高,并具有伸缩性。
普通余数hash
根据某个字段进行取模运算,根据余数值来选择缓存服务器
该方式在扩展时存在问题,从5台服务器增加到6台,mod变成6,运算可以得出,之前的余数值全部失效,这样设计就不具有用户粘性了,在缓存大量失效的情况下,数据库的压力就会激增,可能导致数据库宕机或业务中断。
一致性hash
一致性hash算法通过一致性hash环来实现,先构造一个长度为0~2的32次方的整数环,再根据hash算法将缓存服务器映射到这个hash环上。当用户来查询时,将用户特征同样通过hash算法映射到hash环上,然后顺时针查询离这个key最近的缓存服务器节点。
当需要增加缓存服务器的时候,只需要将新的缓存服务器映射到hash环上,影响范围很小,如下图所示,新加入NODE3节点,NODE0节点和NODE2节点不受影响,理论上NODE3节点只分担了NODE1节点50%的流量。
理论上上述请求的命中率是100% - 33% * 50% = 83.5%,且缓存服务器越多,新加入服务器后命中的概率越高。
但是上述算法也存在一定的问题,NODE3节点的加入只分担了NODE1的流量,这就相当于NODE0和NODE1节点的压力是NODE1和NODE3的两倍,如何做到负载均衡?
负载不均问题
计算机领域有句话,计算机的任何问题都可以通过增加一个虚拟层来解决。计算机硬件、计算机网络、计算机软件都是如此。计算机网络的7层协议,每一层都是下一层的虚拟层;计算机操作系统可以看做计算机硬件的虚拟层;jvm可以看做是操作系统的虚拟层;分层的计算机软件架构也是利用虚拟层的概念。
解决一致性hash的负载不均问题,也可以利用虚拟层的概念,将每台物理缓存服务器虚拟成一组虚拟缓存服务器,再将虚拟的hash值放在hash环上。每次查询时,先找到虚拟的缓存服务器key值,再通过该值找到物理服务器信息。
这样新加入缓存服务器时,是加入一组虚拟服务器,如果数量足够多的话,这个新加入的缓存服务器会均匀的影响原有的缓存服务器。
每个物理节点对应的虚拟节点越多,各个物理节点之间负载就越均衡,新加入的物理服务器对原有的物理服务器影响越保持一致(这就是一致性hash的名称的由来)。
在实践中,一台物理服务器虚拟多少虚拟服务器比较合适呢,太多会影响性能,太少又会负载不均衡,一般来说,经验值是150,但是也要根提业务和情况来定。
分布式缓存实践中,一致性hash是一个比较成熟的算法,不过还有其他优秀的算法,例如redis cluster的hash slot算法。