分布式过程当中咱们将服务分散到若干的节点上,以此经过集体的力量提高服务的目的。然而,对于一个客户端来讲,该由哪一个节点服务呢?或者说对某个节点来讲他分配到哪些任务呢?git
考虑到单服务器不能承载,所以使用了分布式架构,最初的算法为 hash() mod n, hash()一般取用户ID,n为节点数。此方法容易实现且可以知足运营要求。缺点是当单点发生故障时,系统没法自动恢复。一样不也不能进行动态增长节点。github
为了解决单点故障,使用 hash() mod (n/m)
, 算法
这样任意一个用户都有 m 个服务器备选,可由 client 随机选取。缓存
因为不一样服务器之间的用户须要彼此交互,因此全部的服务器须要确切的知道用户所在的位置。服务器
所以用户位置被保存到 memcached 中。当一台发生故障,client 能够自动切换到对应 backup,因为切换前另外 1 台没有用户的 session,所以须要 client 自行从新登陆。session
他比强哈希的好处是:解决了单点问题。架构
但存在如下问题:负载不均衡,尤为是单台发生故障后剩下一台会压力过大;不能动态增删节点;节点发生故障时须要 client 从新登陆分布式
一致性 hash 算法提出了在动态变化的 Cache 环境中,断定哈希算法好坏的四个定义:ide
平衡性是指哈希的结果可以尽量分布到全部的缓冲中去,这样可使得全部的缓冲空间都获得利用。不少哈希算法都可以知足这一条件。memcached
单调性是指若是已经有一些内容经过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应可以保证原有已分配的内容能够被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其余缓冲区。
在分布式环境中,终端有可能看不到全部的缓冲,而是只能看到其中的一部分。
当终端但愿经过哈希过程将内容映射到缓冲上时,因为不一样终端所见的缓冲范围有可能不一样,从而致使哈希的结果不一致,最终的结果是相同的内容被不一样的终端映射到不一样的缓冲区中。
这种状况显然是应该避免的,由于它致使相同内容被存储到不一样缓冲中去,下降了系统存储的效率。分散性的定义就是上述状况发生的严重程度。好的哈希算法应可以尽可能避免不一致的状况发生,也就是尽可能下降分散性。
负载问题其实是从另外一个角度看待分散性问题。既然不一样的终端可能将相同的内容映射到不一样的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不一样的用户映射为不一样的内容。
与分散性同样,这种状况也是应当避免的,所以好的哈希算法应可以尽可能下降缓冲的负荷。
普通的哈希算法(也称硬哈希)采用简单取模的方式,将机器进行散列,这在cache环境不变的状况下能取得让人满意的结果,可是当cache环境动态变化时,
这种静态取模的方式显然就不知足单调性的要求(当增长或减小一台机子时,几乎全部的存储内容都要被从新散列到别的缓冲区中)。
一致性哈希算法有多种具体的实现,包括 Chord 算法,KAD 算法等实现,以上的算法的实现都比较复杂。
这里介绍一种网上广为流传的一致性哈希算法的基本实现原理,感兴趣的同窗能够根据上面的连接或者去网上查询更详细的资料。
一致性哈希算法的基本实现原理是将机器节点和key值都按照同样的hash算法映射到一个0~2^32
的圆环上。
当有一个写入缓存的请求到来时,计算 Key 值 k 对应的哈希值 Hash(k),若是该值正好对应以前某个机器节点的 Hash 值,则直接写入该机器节点,
若是没有对应的机器节点,则顺时针查找下一个节点,进行写入,若是超过 2^32
还没找到对应节点,则从0开始查找(由于是环状结构)。
如图 1 所示:
图 1 中 Key K 的哈希值在 A 与 B 之间,因而 K 就由节点B来处理。
另外具体机器映射时,还能够根据处理能力不一样,将一个实体节点映射到多个虚拟节点。
通过一致性哈希算法散列以后,当有新的机器加入时,将只影响一台机器的存储状况,
例如新加入的节点H的散列在 B 与 C 之间,则原先由 C 处理的一些数据可能将移至 H 处理,
而其余全部节点的处理状况都将保持不变,所以表现出很好的单调性。
而若是删除一台机器,例如删除 C 节点,此时原来由 C 处理的数据将移至 D 节点,而其它节点的处理状况仍然不变。
而因为在机器节点散列和缓冲内容散列时都采用了同一种散列算法,所以也很好得下降了分散性和负载。
而经过引入虚拟节点的方式,也大大提升了平衡性。