我们在讲解一致性哈希前,先来了解缓存的应用场景,这样,我们才能理解得更为透彻,而不是看完转眼就忘。
假设我们现在手头上有两台服务器,分别为A,B,用来存储文件2,文件3,文件4,文件5。我们希望这几个文件均匀分布在两台服务器上,以便缓解服务器压力并提升查询速度。同学们都知道这时应该用哈希,用哈希函数对缓存服务器的数量进行取模操作,这样我们就可以用这个结果来决定这些文件分别放在哪些服务器上。用到的公式如下
hash(文件名)%n n即为服务器数量
当我们需要查找这个文件时,只需要再次hash这个文件名并对服务器数量进行取模,就可以知道这个文件存在哪个服务器上
示例图如下
随着业务发展要缓存的文件数量越来越多,显然两个服务器已经不能满足我们的需求。我们只能增设一台服务器,这时,服务器数量n变为3,但是问题来了,如果我们依然用上述方法对同一文件进行缓存,那么这个文件所在服务器编号必定与只有两台服务器时的文件编号不同,因为除数变为3,被除数却没变,得出的结果自然改变,如图示意:
可以看到上面新加了一个服务器以后所有数字的分布都发生了变化,换句话说,当服务器数量发生了改变的时候,所有缓存的文件在一定程度上是失效的,同理,一台服务器崩溃也会造成这样的缓存失效后果。我们只能停止服务器运营等所有文件重新在分布一次才能继续对外提供服务。
现在,是时候来了解一下一致性哈希算法了。
其实,说白了,一致性哈希算法也是取模操作,只是我们刚才的取模对象是实际存在的服务器数量,而现在,我们的取模对象并不是实际存在的服务器了,而是假想服务器,什么意思呢?别急,我们继续来聊
假设我们现在有7个假想服务器,而真实存在的服务器为两个,我们按照传统的命名习惯,将假想服务器从0-6命名,而真实服务器就为3和6。现在每个文件分到的服务器很可能是假想的,也就是不存在的,那么就往下找到第一个真实存在的服务器并放进去。这样文件3是直接经由取模操作放到真实服务器上的,文件2本是映射到假想服务器上的,往下找到真实服务器3并放入。而文件4和文件5同理是被分到了编号为6的服务器上的。
示意图如下:
这时候假设业务又发展了,又有钱增设服务器了,编号为4,而取模方法不变,还是包含所有真实服务器的假想服务器总数,即为7。示意图如下:
因为编号3服务器都是存放取模操作后小于等于3的文件,新增进来的4号服务器从6号服务器手中接管取模为4的文件,在这里即为文件4。
大家应该也发现了,在一致性哈希算法中,我们增设和撤销服务器都只会调整一个服务器里的文件就可以进行重新分布,将损失降为了最低。
这样增设一个服务器只需要和他后面的服务器同步数据就可以工作,下线一个服务器只需要将他的数据同步到后面一台服务器再下线。如果不幸突然崩溃了一台服务器也只会影响到该服务器上的数据而已,因为假想服务器不变,其他文件取模操作将得到跟之前同样的结果。我觉得实现中完全可以在每台服务器上同步备份一份前面服务器的数据,这样即使掉线也不会有大的影响。
可能有的同学会问了,如果编号6的服务器下线了怎么办,他已经是最后一个服务器了,里面的数据该存放在哪里?
不知大家是否还记得队列,大部分队列的实现其实都是环形队列,也就是我们常说的循环队列。
这样编号3服务器的就可能成为编号6服务器的托管了。
另外,大部分的一致性哈希算法的实现都是对2^32取模,也就是我们有2^32台假想服务器,也就是说我们的公式为
hash(文件名)%2^32
一致性哈希就讲解到这里,因本人技术有限,撰写此文仅仅是起到记录和扫盲作用,具体说明请翻阅权威专业文献。如有错误,还请赐教。