hash算法
是麻省理工学院1997年提出的一种算法,主要应用于分布式缓存当中,一致性hash算法可以有效地解决分布式存储结构下动态增加和删除节点带来的问题;
传统的hash算法
传统的hash算法是对hash结果取余数(hash(x) / N):对机器编号从0到N-1,按照自定义的hash算法,对每个请求的hash值按N取模得到余数i,然后将请求分发到编号为i的机器;例如hash(“xxx”) % 3 = [0, 1, 2],最终落在三台范围内
传统的hash算法的缺陷
如果一台机器宕机,那么应该落在该机器的请求就无法得到正确的处理,这是需要将宕机的服务提出,重新计算(hash(x) / (N-1))的值,这意味着大量缓存的失效或者数据需要转移;
宕机一台机器,会有(N-1)/N的机器的缓存数据需要重新进行计算;3 – > 2/3 节点缓存失效
新增一台机器,会有N/(N+1)的服务器的缓存数据需要进行重新计算;4 – > 3/4 节点缓存失效
一致性hash算法
一致性Hash算法也是使用取模的方式,只是对2^32 取模,相当于(hash(x) / (2^32 )),简单来说,一致性hash算法将整个哈希区间组织成一个虚拟的圆环,如建设某哈希函数H的值区间为0-2^32-1(j即哈希值是一个32位无符号整型),整个哈希环如下:
整个区间按顺时针方向形成一个圆环,圆环正上方的点代表0,0Ian右侧的第一个点代表1,以此类推,2、3、4、5、6…直到2^32-1,也就是说0点左侧的第一个点代表 232-1,0和232-1在0点方向重合,我们把这个由 2^32个点组成的圆环称为Hash环;
下一步将各个服务器使用Hash算法进行哈希,具体可以选择服务器的IP或者主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置,这里假设有4台服务器使用IP地址哈希后在环区间的位置如下:
接下来使用如下算法定位数据访问到的响应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器;
例如我们有Object A、Object B、Object C、Object D四个数据对象,经哈希计算后,在环区间上的位置如下:
经过一致性Hash算法,数据A会被定位到Node A上,B被定位到Node B上,C被定位到Node C上,D被定位到Node D上;
一致性Hash算法的扩容与容错
现在假设Node C宕机,此时对象A、B、D不会受到影响,只有C对象被重新定位到Node D,一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其他数据不会受到影响,如下所示:
如果在系统中增加一台服务器NodeX,如下图所示:
此时对象ObjectA、B、D不受影响,只有对象C需要重新定位到新的Node X,一般的,在一致性Hash算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间的数据,其他数据也不会受到影响;
综上所述,一致性Hash算法对于节点的增减都只需要重新定位环区间中的一小部分数据,具有较好的容错性和可扩展性;
Hash环的数据倾斜问题
一致性Hash算法在服务节点较少时,容易因为节点分部不均匀而造成数据倾斜问题(被缓存的数据大部分集中在某一台服务器上),比如系统中只有两台服务器,其环分布如下:
此时造成大量的数据集中在Node A上,而只有极其少量会定位到Node B上,为了解决这种数据倾斜问题,一致性Hash算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点,具体做法可以在服务器IP或主机名的后面增加编号来实现。
比如可以为每台服务器计算三个虚拟节点,于是可以分别计算
Node A#1 、Node A#2 、Node A#3 ,有两台服务器,于是形成了6个虚拟节点
同事数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,如果定位到Node A#1 、Node A#2 、Node A#3三个虚拟节点的数据均匀定位到Node A上,这样就解决了服务节点少时数据倾斜的问题;在实际应用中,通常将虚拟节点的数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分部;xxl-job设置的是100