一致性Hash(Consistent Hashing)
一、产生背景
在业务开发中,我们常把数据持久化到数据库中。如果需要读取这些数据,除了直接从数据库中读取外,为了减轻数据库的访问压力以及提高访问速度,我们更多地引入缓存来对数据进行存取。读取数据的过程一般为:先访问缓存,如果缓存存在就从缓存中读取数据,如果缓存不存在就从数据库中读取数据,然后将数据写入缓存
例如有n个服务器,m为机器编号,o为对象名称,我们要将一个对象均匀映射到n个服务器上,采用下面这个式子来定位对象缓存服务器:
m = hash(o) mod n
但是运用这种方式有一定的弊端,比如当机器需要扩容或者机器宕机的时候,我们需要重新计算所有数据的hash值cache会大规模的实效,数据的访问会直接访问后面的数据库服务器。一致性hash算法正是为了解决此类问题的方法,它可以保证当机器增加或者减少时,对缓存访问命中的概率影响减至很小。
二、一致性Hash环
这个环的起点是0,终点是2^32 - 1,且起点与终点连接,环的中间的整数按逆时针分布,故这个环的整数分布范围是[0, 2^32-1],如图所示:
(一)将对象放置到Hash环
假设有4个对象o1,o2,o3,o4。然后使用hash得到4个对象的hash值m1,m2,m3,m4,将这四个值放到hash环上面,如图:
(二)将机器放置到Hash环
使用同样的hash函数,将机器放到hash环上面,假设缓存服务器为c1,c2,c3,得到的hash值为t1,t2,t3,如图:
(三)为对象选择机器
将对象和机器都放置到同一个hash环后,在hash环上顺时针查找距离这个对象的hash值最近的机器,即是这个对象所属的机器。如图:
(四)处理机器增减的情况
例如增加机器c4的部署并将机器c4加入到hash环的机器c3与c2之间。这时,只有机器c3与c4之间的对象需要重新分配新的机器。对于我们的例子,只有对象o4被重新分配到了c4,其他对象仍在原有机器上。
或者机器c1下线(当然,也有可能是机器c1宕机),这时,只有原有被分配到机器c1对象需要被重新分配到新的机器。对于我们的例子,只有对象o2被重新分配到机器c3,其他对象仍在原有机器上。如图:
(五)虚拟节点
不过从上面可以看出来新加入的机器c4只分担了机器c2的负载,机器c1与c3的负载并没有因为机器c4的加入而减少负载压力。如果4台机器的性能是一样的,那么这种结果并不是我们想要的。所以我们因入虚拟节点来解决负载不均衡的问题。
将每台物理机器虚拟为一组虚拟机器,将虚拟机器放置到hash环上,如果需要确定对象的机器,先确定对象的虚拟机器,再由虚拟机器确定物理机器。
假如开始时存在缓存机器c1,c2,c3,对于每个缓存机器,都有3个虚拟节点对应,其一致性hash环结构如图:
假设对于对象o1,其对应的虚拟节点为c11,而虚