写在前面
  在学习Redis的集群内容时,看到这么一句话:Redis并没有使用一致性hash算法,而是引入哈希槽的概念。而分布式缓存Memcached则是使用分布式一致性hash算法来实现分布式存储。所以就专门学习了一下

什么是分布式?什么是一致性?什么是哈希?
  1)分布式(distributed)是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务。“分布式一致性hash算法”中的“分布式”就是指缓存数据的分布性。

  

  

  集中式将一个系统的所有服务模块部署到了不同的服务器上,构成一个集群,通过负载均衡设备对外提供服务。集中式部署就像茶水间同时有多个饮水机提供服务,服务冗余部署。分布式部署则将系统拆分成不同的服务模块,然后将不同的服务模块部署在不同的服务器上。

  2)一致性Wiki的定义:

  Consistent hashing is a special kind of hashing such that when a hash table is resized, only K/n keys need to be remapped on average, where K is the number of keys, and n is the number of slots. In contrast, in most traditional hash tables, a change in the number of array slots causes nearly all keys to be remapped because the mapping between the keys and the slots is defined by a modular operation.

  大概意思就是“一致性哈希是一种特殊的哈希算法,提供了这样的一个哈希表,当重新调整大小的时候,平均只有部分(k/n)key需要重新映射哈希槽,而不像传统哈希表那样几乎所有key需要需要重新映射哈希槽”。一致性hash能保证在分布式环境中,对key进行哈希的结果或者说key与节点之间的映射关系不会受节点的增加和删除而产生重大的变化。

  3)hash,俗称“哈希”,也叫散列,是一种将任意长度的消息(数据)压缩到某一固定长度的消息摘要(数据)的算法。常见的hash算法有MD5,SHA等。hash算法具有几个重要的特性:不可逆性(即从hash值反推出原消息是不可能的)、抗冲突性(即给定消息M1,不存在另一个消息M2,使得Hash(M1)=Hash(M2))和分布均匀性(即hash的结果是均匀分布的)。Memcached中,存取数据时都要进行哈希映射。正是这几个特性,保证了memcached缓存中key值得唯一性。

分布式一致性hash算法使用背景
  我们已经知道,memcached的分布式主要在于客户端的分布式算法。memcached客户端就像一个网络中的路由,经过特定的算法将数据分散的存在到memcached服务端的机器上,又从分散的memcached服务端的机器上提取数据。实际中,常见的存储和提取数据的算法有取模算法和本文分析的一致性hash算法。

  取模算法算法的原理是:

hash(key)%N
1
  其中key 代表数据的键,N 代表memcached服务器的数量。取模的结果就是memcached客户端要定位的memcached服务器。取模算法很明显,结果受N的影响,当服务器数量N增加或者减少的时候,原先的缓存数据定位几乎失效,缓存数据定位失效意味着要到数据库重新查询,这对于高并发的系统来说是致命的。于是,人们提出了一致性hash算法,最终目的是实现在移除、添加一个memcached服务器时对已经存在的缓存数据的定位影响尽可能的降到最小。

环形hash空间
  通常,一个缓存数据的key经过hash后会得到一个32位的值,也就是0~2^32 - 1数值范围。我们可以把这个数值范围抽象成一个首尾相连环形的空间,我们称这个空间为环形hash空间。如下图所示:

  

映射key、server节点到环形hash空间
  有了环形hash空间之后,缓存数据的key经过hash后得到的值就映射到了环形hash空间。假设有key1、key2、key3、key4,经过hash后,映射到环形hash空间如下图所示。同理,我们可以把memcached服务器抽象成网络上的节点经过hash后映射到环形hash空间。假设有server1(可以是服务器的某些唯一标志信息,如ip等)、server2、server3

  

映射key到server节点
  现在缓存key和server节点都经过一致性hash算法映射到了环形hash空间,现在就可以将缓存key和server节点的关系进行映射了。顺时针沿着环形hash空间,从某个缓存key开始,直到遇到一个server节点,那么该缓存key就存储到这个server节点上。如图:

  

  了解了key、server节点、hash空间之间的映射关系之后,已经清楚了缓存数据是怎样分布的存储到memcached服务器了。查找缓存数据的时候,也采用同样的映射方法来定位。

删除server节点
  当在server集群中删除server2节点时,受影响的也仅是server1~server2之间的缓存数据Key3,这部分数据需要重新到数据库查找再次映射到server3节点上。如下图所示:

  

添加server节点
  现在我们已经知道memcached存储和访问数据的策略了。那么当在server集群中增加一个server节点时,对数据访问的命中率又有什么影响呢。如下图,在server1和server2节点之间增加一个节点server4

  

  从上图可以看出,增加server4节点后,原有的缓存数据分布中,仅有server1和server4节点之间的数据进行了重新分布,这部分数据需要重新到数据库查找再次映射到新添加的server4节点上。尽管不能命中的缓存数据仍然存在,但相对于取模算法,已经是最大限度地抑制了hash键的重新分布。

虚拟节点的引入
  我们已经知道,添加和删除节点都会影响缓存数据的分布。尽管hash算法具有分布均匀的特性,但是当集群中server数量很少时,他们可能在环中的分布并不是特别均匀,进而导致缓存数据不能均匀分布到所有的server上。

  为解决这个问题,需要使用虚拟节点的思想:为每个物理节点(server)在环上分配100~200个点,这样环上的节点较多,就能抑制分布不均匀。当为cache定位目标server时,如果定位到虚拟节点上,就表示cache真正的存储位置是在该虚拟节点代表的实际物理server上。另外,如果每个实际server节点的负载能力不同,可以赋予不同的权重,根据权重分配不同数量的虚拟节点。

  虚拟节点的hash计算可以采用对应节点的 IP 地址加数字后缀的方式。例如假设 serverA 的 IP 地址为 127.0.0.1 。

  引入虚拟节点前,计算serverA 的 hash 值:

hash(“127.0.0.1”);
1
  引入虚拟节点后,计算虚拟节点serverA1 和 serverA2 的 hash 值:

hash(“127.0.0.1#1”);
hash(“127.0.0.1#2”);
1
2
一致性hash算法与取模算法的比较
  取模算法的方法简单,数据的分散性也可以,但其主要缺点是当添加或移除server节点时,缓存重新映射的代价相当巨大。添加或移除server节点时,余数就会产生巨变,这样就无法定位与存储时相同的server节点,从而影响缓存的命中率。

  而一致性hash算法则最大限度的减少了server节点变化带来的影响,当节点变化时,只影响一个server节点的部分数据,且hash算法能够保证需要重新分布的缓存数据能映射到新的server节点中。
---------------------

原文:https://blog.csdn.net/u011489043/article/details/78944985

最新文章

  1. [译文]选择使用正确的 Markdown Parser
  2. windows + python + dlib
  3. hadoop 多表join:Map side join及Reduce side join范例
  4. selenium-JS点击(项目应用)
  5. Redis - 发布和订阅
  6. System.getProperty()方法获取大全
  7. 5.7 NDK开发
  8. Java-struts2 通过MODEL接收表单数据的方法
  9. 【原创】Android 从一个Activity跳转到另外一个Activity
  10. vim的命令集合
  11. css-布局1-基本属性
  12. 15+优秀的jQuery视差插件
  13. 引用iscroll的一个封装方法
  14. Oracl Over函数
  15. dijistra
  16. Django信息安全相关之CSRF和XSS
  17. java基本数据类型与封装类型详解(int和Integer区别)
  18. HDFS NameNode内存详解
  19. MyBatis框架的基本使用
  20. 【转】Java中的内部类和匿名类

热门文章

  1. One point compactification
  2. PyCharm安装使用 激活码
  3. 1900型USB接口扫描枪设置虚拟串口模式提升扫描速度
  4. 无法删除另一个分区的windows文件夹
  5. linux后台执行./run.py提示python syntax error near unexpected token `('
  6. 如何确定系统上的CPU插槽数量
  7. 2018牛客网暑假ACM多校训练赛(第四场)E Skyline 线段树 扫描线
  8. 利用Flume将MySQL表数据准实时抽取到HDFS
  9. Badboy录制Jmter脚本
  10. JS事件基础