聊聊一致性哈希算法的实现

以前看过了解过一致性哈希算法的实现原理,现在真正要用代码实现时,还是有好几个细节需要确定的。

假设我们的目标是:有N个节点,新建的资源要均匀分配到这些节点上。另外为了在节点少的情况下让分配更均衡,需要加上虚拟节点的概念。

那大概的实现思路是:定义一个哈希算法,定义虚拟节点的副本数为K。首先计算好这N个节点的K个虚拟节点的哈希值,这N*K个哈希值从小到大排序,当新建一个资源,就需要使用相同的哈希算法计算出这个资源的哈希值H。然后根据哈希值H找到是在N*K个哈希值中排在哪个虚拟节点所在位置(即从小到大排序后,大于且最靠近哈希值H的那个哈希值),然后确定这个虚拟节点是属于哪个节点,则资源就分配给这个节点。

思路明确后,下面有几个待确定点:

1. 哈希算法用啥实现?

2. 虚拟节点的副本数K取多少?

3. 拿节点的什么信息来算哈希值?

这里应该是拿虚拟节点的信息来计算

4. 拿新资源的什么信息来算哈希值?

5. 怎么确定新资源是应该分配给哪个节点?

6. 虚拟节点计算好的哈希值,是在哪里存储?数据库表?redis?内存?

 

之前也没有实现过一致性哈希算法,当然。。也是先从网上搜一堆然后各种比较一番,结合自己的需要,实现如下:

代码是python实现的

1. 哈希算法的实现

有的说用md5、FNV1_32_HASH、KETAMA_HASH

这里用的是CRC32校验,返回值范围是 [-2^31, 2^31-1],再取abs绝对值,则范围为 [0, 2^31-1],跟理论上说的长度为2^32的整数环相匹配

from zlib import crc32

def hash(hash_name):
    hash_bytes = bytes(hash_name)
    return abs(crc32(hash_bytes))

这里有个疑惑,abs()会返回绝对值,那对于计算出负数的hash值会不会影响了哈希的均衡性?

 

2. 虚拟节点副本数的设置

理论上,虚拟节点副本数设置地越多,则对应节点分配地越均衡。但虚拟节点数过多,会导致新资源在查找需要分配到哪个虚拟节点的这个过程会很久。所以这里存在着均衡性和查找效率上的一个权衡。

这里我们简单的设置副本数为3,即一个节点对应3个虚拟节点。后面也通过一个简单测试来验证这个设置。

 

3和4 节点和新资源的哈希值的计算

(1)这里其实是确定1中hash函数的哈希名hash_name该怎么设置,设置的原则是能够唯一标识这个节点或新资源,使得不同的节点或新资源计算出来的哈希值肯定是不一致的。如果节点或新资源有唯一的IP,取hash_name = ip是可以的,如果没有,则利用其它的信息的组合,来进行唯一标识,比如节点名+节点uuid的组合。

(2)假设节点有个唯一的IP,那它K个虚拟节点的哈希名该怎么设置?

是取IP+递增数字,比如IP_0、IP_1、IP_2...,或者取IP_随机数

这里是采用IP+递增数字,也是通过后面一个简单测试的结果来决定的。

 

5. 查找新资源所分配的节点

在原理上说,是从一个整数环中顺时针找到离新资源最近(第一个大于或等于新资源的哈希值H)的一个虚拟节点,从而决定是分配到哪一个节点。

转化成代码实现,就是在一个有序集合中进行查找某个值,实现思路有很多,这里简单的实现:二分查找法

先对所有的虚拟节点的哈希值从小到大排序,形成一个数组hash_arr,然后再二分查找到所分配到的虚拟节点

 

6. 虚拟节点的哈希值的存储

采用数据库表来预先存储好虚拟节点的哈希值

 

以上各个细节设置完,还是有点担心这个算法实际的均衡性,只能做个初步的测试进行验证:

可用来调整的变化参数有:哈希算法、虚拟节点的副本数、哈希名的设置规则,这几个参数采用不同的方法或值进行组合,都会对最终的均衡性产生影响。

由于对哈希算法没啥研究,就还是采用CRC32校验。

测试样例:调整虚拟节点的副本数和哈希名的设置规则

测试数据:新资源6000个,节点6个

理想结果:每个节点分配到1000个新资源

测试结果:

(1)哈希名的设置规则为:IP+递增数字

调整虚拟节点的副本数为3、5、10,从测试结果来看,没有绝对的均衡,分配最少的节点有近600个新资源,分配最多的有1500多个。而副本数增大,均衡性并没有明显变好,有可能是副本数还不够大,比如设置为1000、10000。

(2)哈希名的设置规则为:IP+10位随机字符串

调整虚拟节点的副本数为3、5、10,从测试结果来看,均衡性有波动,可能是跟这个随机的字符串有关。最差的情况下,分配最少的节点有400多个新资源,分配最多的有1700多个。副本数增大,没有显示出线性增长的关系。

测试结论:

分配的均衡性,与哈希算法+虚拟节点的副本数+哈希名的设置规则这几个组合在一起有关,有兴趣的可以研究研究。

最终采取的组合还是:哈希算法为CRC32校验,虚拟节点的副本数为3,哈希名的设置规则为唯一标识+递增数字

 

你可能感兴趣的:(重构,python)