1.hash算法
哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。
2.普通的hash算法在分布式应用中的不足
在分布式的存储系统中,要将数据存储到具体的节点上,如果采用普通的hash算法进行路由,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了。
3.一致性hash算法
一致性哈希算法也是使用取模的方法,不是对服务器的数量进行取模,而对2 的32次方取模,形成一个环形Hash空间,即0~(2^32)-1的数字空间中。现在可以将这些数字头尾相连,想象成一个闭合的环形。
把数据对象通hash算法计算出对应的key值,然后散列到Hash环上,将机器通过hash算法映射到环上,(一般情况下对机器的hash计算是采用机器的IP或者机器唯一的别名作为输入值),这样数据对象与机器处于同一哈希空间中,然后以顺时针的方向计算,将所有数据对象存储到离自己最近的机器中。在这样的部署环境中,hash环是不会变更的,因此,通过算出对象的hash值就能快速的定位到对应的机器中,这样就能找到对象真正的存储位置了。如果某个服务器故障,则则原属于此服务器的数据对象会按照顺时针缓存到下一服务器中,与传统hash算法相比,只是部分缓存失效,不会造成所有缓存失效。
4.hash环的偏斜
hash算法是不保证平衡的,现实中的服务器节点可能在hash环上并不均匀分布,可能会造成数据对象更多缓存到某几台节点,造成缓存的分布不均匀,在极端情况下,仍然有可能引起系统的崩溃,这称为hash环的偏斜。虚拟节点"解决了这个问题。
5.虚拟节点
将现有的物理节点通过虚拟的方法复制出来,这些由实际节点虚拟复制而来的节点被称为"虚拟节点"。个实际节点可以对应多个虚拟节点。虚拟节点越多,hash环上的节点就越多,缓存被均匀分布的概率就越大。
虚拟节点”的hash计算可以采用对应节点的IP地址加数字后缀的方式。例如假设NODE1的IP地址为192.168.1.100。引入“虚拟节点”前,计算 cache A 的 hash 值:
Hash(“192.168.1.100”);
引入“虚拟节点”后,计算“虚拟节”点NODE1-1和NODE1-2的hash值:
Hash(“192.168.1.100#1”); // NODE1-1
Hash(“192.168.1.100#2”); // NODE1-2
6.4个适应条件
一致性哈希提出了在动态变化的Cache环境中,哈希算法应该满足的4个适应条件
-
均衡性(Balance)
平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。 -
单调性(Monotonicity)
单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。(这段翻译信息有负面价值的,当缓冲区大小变化时一致性哈希(Consistent hashing)尽量保护已分配的内容不会被重新映射到新缓冲区。) -
分散性(Spread)
在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。 -
负载(Load)
负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。