很多Memcache客户端,都使用一致性HASH算法(Consistent Hashing)来实现对Memcache的分布式管理。使用一致性HASH算法可尽可能的降低缓存系统伸缩(比如添加、移除缓存服务器)时原HASH映射关系的变化率。

       通常,值进行集合映射最简单的办法是CRC32散列后取模,这个模数是被映射到的集合范围。但是,当模数变化时,原有映射关系也发生明显的变化,在分布式缓存系统中,会造成缓存大范围的失效。

       使用一致性HASH,节点故障时,缓存失效的范围可以缩小到相应故障节点影响的范围,而不会对其它的节点产生影响。新增节点时,影响范围可以缩小到只影响下一个节点(我们认为集群中的所有缓存服务器节点都被部署在一个环形上,范围是0到2^32-1)部分缓存数据的命中,因为未被命中的数据被认为分布在了新的服务器节点上。

       节点映射情况如下:

       memcache与一致性HASH算法_php


       首先通过HASH运算,将服务器缓存节点散列在一个0到2^32-1范围内的一个环上,也就是4字节存储无符号数据类型的空间,散列值通过crc32哈唏函数计算得到。

       对于所要存储的数据,系统对KEY也通过crc32进行HASH运算,同样得到一个0到2^32-1范围内的HASH值,根据这个HASH值所落环上的位置,寻找最近的下一个数据缓存节点进行数据缓存。比如图所示:key1, key2落在了svr2上,key3, key4, key5落到了svr3上,而key6, key7落到了svr4上。当然,如果key计算出来的HASH值找不到比它大的HASH缓存节点,则该key将落在第一个缓存节点上,也正因为此,形成了一个环形的数据结构。

       使用这种数据结构,如果svr2挂了,则只会影响key1, key2缓存的命中,并不会影响其它缓存数据的命中,同时key1, key2新的数据会被映射到svr3上。如果新增一台缓存服务器svr+,假设被映射到svr3和svr4之间,则此时仅仅会影响svr3到svr+之间数据的命中,当然了,下一次这个区间的数据会被缓存在svr+上。

       使用一致性HASH算法的最大特色是新增、删除缓存节点时,避免对其它节点的影响。很显然,缓存服务器越多,每台服务器所管辖的区间就会越小,故障时所产生的影响也会越小。

       

       虽说如此,但CRC32算法并无法保证每台缓存服务器可以均匀的分布在HASH环上,也许有的管辖区间大,有的管辖的区间小,这样就对数据存储的平衡性带来了影响。同样,硬件好的服务器希望多干点活,次点的服务器希望少干点活,这种需要也是常见的。为了提高数据的平衡性,又引入了虚拟节点的概念。

       例如:

       有三台服务器SVR1, SVR2, SVR3, 我们希望它们服务的权重分别是1:3:5的关系。假设虚拟节点的基数为10,则SVR1对应10个虚拟节点,SVR2对应30个虚拟节点,SVR3对应50个虚拟节点。虚拟节点也是通过CRC32算法散列在不同的点。这样,如果说散列3次拼的是人品,那么散列90次(10+30+50),基本上拼的就是技术了。

       引入虚拟节点后,需要缓存的数据的KEY,经过HASH运算,先找到离它最近的一个虚拟节点,然后根据虚拟节点与具体缓存服务器的对应关系映射到具体的机器上。这样基本上可保持平衡和权重设定的比例。