一致性哈希算法通过一个叫作一致性哈希环的数据结构实现。这个环的起点是 0,终点是2^32 -1
, 并且起点与终点连接,故这个环的整数分布范围是 [0, 2^32-1],如下图所示:
将对象放置到哈希环
假设我们有 “semlinker”、“kakuqo”、“lolo”、“fer” 四个对象,分别简写为 o1、o2、o3 和 o4,然后使用哈希函数计算这个对象的 hash 值,值的范围是 [0, 2^32-1]:
将服务器放置到哈希环
接着使用同样的哈希函数,我们将服务器也放置到哈希环上,可以选择服务器的 IP 或主机名作为键进行哈希,这样每台服务器就能确定其在哈希环上的位置。这里假设我们有 3 台缓存服务器,分别为 cs1、cs2 和 cs3:
为对象选择服务器
将对象和服务器都放置到同一个哈希环后,在哈希环上顺时针查找距离这个对象的 hash 值最近的机器,即是这个对象所属的机器。 以 o2 对象为例,顺序针找到最近的机器是 cs2,故服务器 cs2 会缓存 o2 对象。而服务器 cs1 则缓存 o1,o3 对象,服务器 cs3 则缓存 o4 对象。
服务器增加的情况
假设由于业务需要,我们需要增加一台服务器 cs4,经过同样的 hash 运算,该服务器最终落于 t1 和 t2 服务器之间,具体如下图所示:
对于上述的情况,只有 t1 和 t2 服务器之间的对象需要重新分配。在以上示例中只有 o3 对象需要重新分配,即它被重新到 cs4 服务器。在前面我们已经分析过,如果使用简单的取模方法,当新添加服务器时可能会导致大部分缓存失效,而使用一致性哈希算法后,这种情况得到了较大的改善,因为只有少部分对象需要重新分配。
服务器减少的情况
假设 cs3 服务器出现故障导致服务下线,这时原本存储于 cs3 服务器的对象 o4,需要被重新分配至 cs2 服务器,其它对象仍存储在原有的机器上。
虚拟节点
到这里一致性哈希的基本原理已经介绍完了,但对于新增服务器的情况还存在一些问题。新增的服务器 cs4 只分担了 cs1 服务器的负载,服务器 cs2 和 cs3 并没有因为 cs4 服务器的加入而减少负载压力。如果 cs4 服务器的性能与原有服务器的性能一致甚至可能更高,那么这种结果并不是我们所期望的。
针对这个问题,我们可以通过引入虚拟节点来解决负载不均衡的问题。即将每台物理服务器虚拟为一组虚拟服务器,将虚拟服务器放置到哈希环上,如果要确定对象的服务器,需先确定对象的虚拟服务器,再由虚拟服务器确定物理服务器。