一致性哈希

学习分布式, 一致性哈希是最最基础的知识, 所以要理解好.

那什么是一致性哈希呢?(what)

百度百科 上的解释很专业术语. 要一句话定义貌似也有难度: 一致性哈希算法是在哈希算法基础上,提出的在动态变化的分布式环境中,哈希算法应该满足的几个条件: 平衡性, 单调性和分散性.

1.平衡性是指 hash的结果应该平均分配到各个节点, 这样从算法上就解决了负载均衡问题.

2.单调性是指 在新增或者删减节点时, 同一个key访问到的值总是一样的.

3.分散性是指 数据应该分散的存放在 分布式集群中的各个节点(节点自己可以有备份), 不必要每个节点都存储所有的数据.

为什么要一致性哈希?(why)

这个问题问得很好…首先我们要看看不使用一致性hash, 我们的分布式集群如何工作.

1. 普通集群, 把固定的key映射到固定的节点上, 节点只存放各自key的数据, 如图:

normal

这样, 我们必须维护好key和节点的关系, 而且当其中一个节点挂掉了, 节点上的数据可以迁移, 但key的关系也要重新维护.

2. 简单hash集群. 为了不想维护key, 降低复杂性和其他开销, 很容想到 对key进行hash , 然后对节点数取模, 比如我们原本有四个节点, 如下图

SIMPLE~1

这个时候就不必维护这些key对应的node了, 直接通过hash值, 然后对节点数取模, 看起来貌似很完美, 足够了吧?

No! 如果这个时候其中一个节点挂了, 那这个节点的数据就完全不可用了. 当然你会说可以通过数据迁移呀, 嘿嘿, 问题

恰恰难在数据迁移, 因为这时候挂了, 节点数变为3了, 对key取hash后再 mod 3 的话, 大部分的key对应的节点都要改. 这个时候

只能整个集群的数据都重新迁移一遍才能达到效果, 也许你忙完这些工作, 还不如把挂掉的机器换个新的!!! 再者, 不仅仅是节点挂了会出现问题

如果整个分布式集群负载很高, 希望增加节点来解决问题, 这个时候, 迁移的工作还是一样的麻烦, 这样我估计如果数据量庞大的话, 没人敢轻易迁移.

于是便有了一致性hash

3. 一致性哈希

CONCUR~1

如图, 所有的节点也有自己的key(比如hostname), 经过hash, 然后mod 2的32次方, 映射到这个超大的环上面的一个虚拟节点

然后所有的key去获取value的时候, 也是同样的hash算法, mod 2的32次方, 这时候不一定所有的key都刚好映射到各个节点相应的虚拟

节点上(事实上概率很小), 然后这时候取值只要按照约定好的固定方向(如顺时针), 找到第一个的虚拟节点, 然后根据该虚拟节点

就可以找到相应的node, 然后进行相应的操作.

这个时候, 如果其中一个节点挂了, 那么依然要进行数据迁移, 只不过数据迁移的数据量减少了, 只需要将挂了的节点的数据迁移到他顺时针的下一个

节点上即可, 这个对应的keys依然能够找到数据. 同样的, 如果增加节点, 数据迁移量也不多, 只需要将该节点逆时针方向到达上一个节点之前的key对应的数据都

迁移到新增的节点上就OK了.这就是传说中的一致性哈希.

比如上图, key1 key2 key3 key4 key5 key6的值将由 node2返回( 假设这是一个key value存储集群)

同样的, key7~key11 对应 node3

key12 ~key17对应node4

key18~key22 对应node1

3. 讲完了what和why, 就是how了

其实上面在why讲解过程中, how的部分已经讲解了一大片, 其实关键的地方还是在hash算法的选择, 如何选择好的hash算法, 让他能够平均地分配每个节点, 这才是

最大的问题.

 

应用场景

这里我先描述一个极其简单的业务场景:用4台Cache服务器缓存所有Object。

那么我将如何把一个Object映射至对应的Cache服务器呢?最简单的方法设置缓存规则:object.hashCode() % 4。

Cache 0:

object.hashCode() % 4 == 0

Cache 1:

object.hashCode() % 4 == 1

Cache 2:

object.hashCode() % 4 == 2

Cache 3:

object.hashCode() % 4 == 2

看起来一切正常,考虑下面两种情况:

一:由于Cache3硬件损坏,所有Cache3上的缓存都失效了,需要把Cache3移除。

二:由于负载已经无法承担业务增涨,决定添加一台Cache服务器。

一致性哈希算法简介

一致性哈希算法是在哈希算法基础上,提出的在动态变化的Cache环境中,哈希算法应该满足的4个适应条件。

平衡性(Balance)

平衡性是指Hash的结果能够尽可能分布均匀,充分利用所有缓存空间

单调性(Monotonicity)

单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

分散性(Spread)

分散性定义了分布式环境中,不同终端通过Hash过程将内容映射至缓存上时,因可见缓存不同,Hash结果不一致,相同的内容被映射至不同的缓冲区。

负载(Load)

负载是对分散性要求的另一个纬度。既然不同的终端可以将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。

使用一致性哈希算法解决上述问题

一致性哈希算法采用一种新的方式来解决问题,不再仅仅依赖object.hashCode()本身,而且将Cache的配置也进行哈希运算。具体步骤如下:

1. 首先求出每个Cache的哈希值,并将其配置到一个0~2^32的圆环区间上。

2. 使用同样的方法求出需要存储对象的哈希值,也将其配置到这个圆环上。

3. 从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个Cache节点上。如果超过2^32仍然找不到Cache节点,就会保存到第一个Cache节点上。

新增Cache服务器

假设在这个环形哈希空间中,Cache5被映射在Cache3和Cache4之间,那么受影响的将仅是沿Cache5逆时针遍历直到下一个Cache(Cache3)之间的对象(它们本来映射到Cache4上)。

移除Cache服务器

假设在这个环形哈希空间中,Cache3被移除,那么受影响的将仅是沿Cache3逆时针遍历直到下一个Cache(Cache2)之间的对象(它们本来映射到Cache3上)。

虚拟Cache服务器

考虑到哈希算法并不是保证绝对的平衡,尤其Cache较少的话,对象并不能被均匀的映射到 Cache上。为了解决这种情况,Consistent Hashing引入了“虚拟节点”的概念: “虚拟节点”是实际节点在环形空间的复制品,一个实际节点对应了若干个“虚拟节点”,这个对应个数也成为“复制个数”,“虚拟节点”在哈希空间中以哈希值排列。

仍以4台Cache服务器为例,在下图中看到,引入虚拟节点,并设置“复制个数”为2后,共有8个“虚拟节点”分部在环形区域上,缓解了映射不均的情况。

引入了“虚拟节点”后,映射关系就从【对象--->Cache服务器】转换成了【对象--->虚拟节点---> Cache服务器】。查询对象所在Cache服务器的映射关系如下图所示。

总结

在我们的web开发应用中的分布式缓存系统里哈希算法承担着系统架构上的关键点。

使用分布更合理的算法可以使得多个服务节点间的负载相对均衡,可以最大程度的避免资源的浪费以及服务器过载。

使用一致性哈希算法,可以最大程度的降低服务硬件环境变化带来的数据迁移代价和风险。

使用更合理的配置策略和算法可以使分布式缓存系统更加高效稳定的为我们整体的应用服务。

简单的话可以使用MD5算法来作为hash算法, 对于各个hash算法的比较, 可以参考下面的文章

http://www.iteye.com/topic/346682

另外参考 http://www.cnblogs.com/liunx/archive/2010/03/24/1693925.html

和这篇 http://blog.csdn.net/x15594/archive/2011/03/23/6270242.aspx

http://portal.acm.org/citation.cfm?id=258660

http://en.wikipedia.org/wiki/Consistent_hashing

http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/

http://weblogs.java.net/blog/2007/11/27/consistent-hashing

http://tech.idv2.com/2008/07/24/memcached-004/

http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx

http://blog.csdn.net/x15594/article/details/6270242

原文地址:https://www.cnblogs.com/cheng07045406/p/3372515.html