哈希、一致性哈希

  哈希,通过哈希函数将关键字与存储位置建立一个对应关系,这样在查找关键字的过程中就没比较进行一个一个比较,而直接定位关键字所在的位置,是一种以空间换取时间的方式。由于所映射的地址空间有限及哈希函数的设置,就是产生冲突,需要建立处理冲突的方法。在一般情况下,冲突只能尽可能的减少,而不能完全避免。那么什么是一个好的哈希呢?通俗点说,好的哈希也许就是能使关键字地址分布均匀,冲突少。下面我们具体一下好的哈希的几个特点:

  哈希算法应满足的特性:

(1)平衡性(Balance)
  平衡性是指将关键字的哈希地址均匀地分布在地址空间中,使地址空间得到充分利用,这是设计哈希的一个基本特性。

(2)单调性(Monotonicity)
  单调性是指当地址空间增大时,通过哈希函数所得到的关键字的哈希地址也能映射的新的地址空间,而不是仅限于原先的地址空间。或等地址空间减少时,也是只能映射到有效的地址空间中。简单的哈希函数往往不能满足此性质,如常用的除留余数法,x=a mod p,在上式中,p表示地址空间的大小。不难看出,当地址空间发生变化时(从p1到p2),原来所有的哈希结果均会发生变化,从而不满足单调性的要求。

(3)分散性(Spread)
  哈希经常用在分布式环境中,终端用户通过哈希函数将自己的内容存到不同的缓冲区。此时,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。

(4)负载(Load)
  负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。

  满足上述条件的一个普遍方式是一致性哈希,经常用于web应用中的缓存。在大型web应用中,缓存可算是当今的一个标准开发配置了。在大规模的缓存应用中,应运而生了分布式缓存系统。分布式缓存系统的基本原理,大家也有所耳闻。key-value如何均匀的分散到集群中?说到此,最常规的方式莫过于hash取模的方式。比如集群中可用机器适量为N,那么key值为K的的数据请求很简单的应该路由到hash(K) mod N对应的机器。的确,这种结构是简单的,也是实用的。但是在一些高速发展的web系统中,这样的解决方案仍有些缺陷。随着系统访问压力的增长,缓存系统不得不通过增加机器节点的方式提高集群的相应速度和数据承载量。增加机器意味着按照hash取模的方式,在增加机器节点的这一时刻,大量的缓存命不中,缓存数据需要重新建立,甚至是进行整体的缓存数据迁移,瞬间会给DB带来极高的系统负载,设置导致DB服务器宕机。那该如何解决?

  一致性哈希,选择具体的机器节点不在只依赖需要缓存数据的key的hash本身了,而是机器节点本身也进行了hash运算

(1)哈希机器节点
  首先求出机器节点的hash值(怎么算机器节点的hash?ip可以作为hash的参数吧。。当然还有其他的方法了),然后将其分布到0~2^32的一个圆环上(顺时针分布)。如下图所示:


 

                                      图 - 1

集群中有4台机器(蓝色大圆圈表示),通过一定的hash算法我们将其分布到如图 - 1所示的环上。

(2)访问方式

  如果有一个写入缓存的请求,其中Key值为K,计算器hash值Hash(K), Hash(K) 对应于图 - 1环中的某一个点,如果该点对应没有映射到具体的某一个机器节点,那么顺时针查找,直到第一次找到有映射机器的节点,该节点就是确定的目标节点,如果超过了2^32仍然找不到节点,则命中第一个机器节点。

(3)增加节点的处理

  增加机器节点之后(如图-2所示),访问策略不改变,依然按照(2)中的方式访问,就可发现,只有在圆环上增加服务节点的位置为逆时针方向的第一个服务节点上的键会受到影响。其他的访问节点一切正常。

              图 - 2


posted @ 2011-11-26 11:53  东方雨中漫步者  阅读(2287)  评论(0编辑  收藏  举报