一致性哈希算法是分布式系统中常用的算法,为什么要用这个算法?

比如:一个分布式存储系统,要将数据存储到具体的节点(服务器)上, 在服务器数量不发生改变的情况下,如果采用普通的hash再对服务器总数量取模的方法(如key%服务器总数量),如果期间有服务器宕机了或者需要增加服务器,问题就出来了。 同一个key经过hash之后,再与服务器总数量取模的结果跟之前的结果会不一样,这就导致了之前保存数据的丢失。因此,引入了一致性Hash(Consistent Hashing)分布算法

把数据用hash函数(如md5,sha1),映射到一个圆环上,如上图所示,数据在存储时,先根据hash算法算出key的hash值,对应到这个环中的位置,如k1对应图中所示的位置同,然后沿着顺时针方向找到服务器节点B,然后把k1在存到B这个节点中。
如果B节点宕机了,则B上的数据就会落到C节点上,如下图所示

这样,只会影响C节点,对于其他节点A、D的数据不会造成影响。但是问题来了,这样会造成C节点负载过重的情况,因为C节点承担了B节点的数据,所以C节点容易宕机,这样造成了分布不均匀。
为了解决这个问题,引入了“虚拟节点“的概念:即想象空上环上有很多”虚拟节点“,一个真实的服务器节点对应多个虚拟节点,数据存储的时候沿着环的顺时针方向找到虚拟节点,就找到了对应的真实服务器节点。如下图

图中的A1、A2、B1、B2、C1、C2、D1、D2都是虚拟节点,机器A负载存储A1、A2的数据,机器B负载存储B1、B2的数据,机器C负载存储C1、C2的数据。由于这些虚拟节点数量很多,均匀分布,因此不会造成“雪崩”现象。
 

一致性哈希算法的PHP实现

下面给出一个接口

  1. /**
  2. * 一致性哈希实现接口
  3. * Interface ConsistentHash
  4. */
  5. interface ConsistentHash
  6. {
  7. //将字符串转为hash值
  8. public function cHash($str);
  9. //添加一台服务器到服务器列表中
  10. public function addServer($server);
  11. //从服务器删除一台服务器
  12. public function removeServer($server);
  13. //在当前的服务器列表中找到合适的服务器存放数据
  14. public function lookup($key);
  15. }
这个接口分别定义了4个方法,cHash(将字符串处理为hash值)、addServer(增加一台服务器)、removeServer(移除一台服务器)、lookup(找到一台服务器来存储数据)
下面给出一个该接口的具体实现
  1. /**
  2. * 具体一致性哈希实现
  3. * author chenqionghe
  4. * Class MyConsistentHash
  5. */
  6. class MyConsistentHash implements ConsistentHash
  7. {
  8. public $serverList = array(); //服务器列列表
  9. public $virtualPos = array(); //虚拟节点的位置
  10. public $virtualPosNum = 5; //每个节点对应5个虚节点
  11. /**
  12. * 将字符串转换成32位无符号整数hash值
  13. * @param $str
  14. * @return int
  15. */
  16. public function cHash($str)
  17. {
  18. $str = md5($str);
  19. return sprintf('%u', crc32($str));
  20. }
  21. /**
  22. * 在当前的服务器列表中找到合适的服务器存放数据
  23. * @param $key 键名
  24. * @return mixed 返回服务器IP地址
  25. */
  26. public function lookup($key)
  27. {
  28. $point = $this->cHash($key);//落点的hash值
  29. $finalServer = current($this->virtualPos);//先取圆环上最小的一个节点当成结果
  30. foreach($this->virtualPos as $pos=>$server)
  31. {
  32. if($point <= $pos)
  33. {
  34. $finalServer = $server;
  35. break;
  36. }
  37. }
  38. reset($this->virtualPos);//重置圆环的指针为第一个
  39. return $finalServer;
  40. }
  41. /**
  42. * 添加一台服务器到服务器列表中
  43. * @param $server 服务器IP地址
  44. * @return bool
  45. */
  46. public function addServer($server)
  47. {
  48. if(!isset($this->serverList[$server]))
  49. {
  50. for($i=0; $i<$this->virtualPosNum; $i++)
  51. {
  52. $pos = $this->cHash($server . '-' . $i);
  53. $this->virtualPos[$pos] = $server;
  54. $this->serverList[$server][] = $pos;
  55. }
  56. ksort($this->virtualPos,SORT_NUMERIC);
  57. }
  58. return TRUE;
  59. }
  60. /**
  61. * 移除一台服务器(循环所有的虚节点,删除值为该服务器地址的虚节点)
  62. * @param $key
  63. * @return bool
  64. */
  65. public function removeServer($key)
  66. {
  67. if(isset($this->serverList[$key]))
  68. {
  69. //删除对应虚节点
  70. foreach($this->serverList[$key] as $pos)
  71. {
  72. unset($this->virtualPos[$pos]);
  73. }
  74. //删除对应服务器
  75. unset($this->serverList[$key]);
  76. }
  77. return TRUE;
  78. }
  79. }
然后, 我们来测试一下该算法

  1. $hashServer = new MyConsistentHash();
  2. $hashServer->addServer('192.168.1.1');
  3. $hashServer->addServer('192.168.1.2');
  4. $hashServer->addServer('192.168.1.3');
  5. $hashServer->addServer('192.168.1.4');
  6. $hashServer->addServer('192.168.1.5');
  7. $hashServer->addServer('192.168.1.6');
  8. $hashServer->addServer('192.168.1.7');
  9. $hashServer->addServer('192.168.1.8');
  10. $hashServer->addServer('192.168.1.9');
  11. $hashServer->addServer('192.168.1.10');
  12. echo "增加十台服务器192.168.1.1~192.168.1.10<br />";
  13. echo "保存 key1 到 server :".$hashServer->lookup('key1') . '<br />';
  14. echo "保存 key2 到 server :".$hashServer->lookup('key2') . '<br />';
  15. echo "保存 key3 到 server :".$hashServer->lookup('key3') . '<br />';
  16. echo "保存 key4 到 server :".$hashServer->lookup('key4') . '<br />';
  17. echo "保存 key5 到 server :".$hashServer->lookup('key5') . '<br />';
  18. echo "保存 key6 到 server :".$hashServer->lookup('key6') . '<br />';
  19. echo "保存 key7 到 server :".$hashServer->lookup('key7') . '<br />';
  20. echo "保存 key8 到 server :".$hashServer->lookup('key8') . '<br />';
  21. echo "保存 key9 到 server :".$hashServer->lookup('key9') . '<br />';
  22. echo "保存 key10 到 server :".$hashServer->lookup('key10') . '<br />';
  23. echo '<hr />';
  24. echo "移除一台服务器192.168.1.2<br />";
  25. $hashServer->removeServer('192.168.1.2');
  26. echo "保存 key1 到 server :".$hashServer->lookup('key1') . '<br />';
  27. echo "保存 key2 到 server :".$hashServer->lookup('key2') . '<br />';
  28. echo "保存 key3 到 server :".$hashServer->lookup('key3') . '<br />';
  29. echo "保存 key4 到 server :".$hashServer->lookup('key4') . '<br />';
  30. echo "保存 key5 到 server :".$hashServer->lookup('key5') . '<br />';
  31. echo "保存 key6 到 server :".$hashServer->lookup('key6') . '<br />';
  32. echo "保存 key7 到 server :".$hashServer->lookup('key7') . '<br />';
  33. echo "保存 key8 到 server :".$hashServer->lookup('key8') . '<br />';
  34. echo "保存 key9 到 server :".$hashServer->lookup('key9') . '<br />';
  35. echo "保存 key10 到 server :".$hashServer->lookup('key10') . '<br />';
  36. echo '<hr />';
  37. echo "移除一台服务器192.168.1.6<br />";
  38. $hashServer->removeServer('192.168.1.6');
  39. echo "保存 key1 到 server :".$hashServer->lookup('key1') . '<br />';
  40. echo "保存 key2 到 server :".$hashServer->lookup('key2') . '<br />';
  41. echo "保存 key3 到 server :".$hashServer->lookup('key3') . '<br />';
  42. echo "保存 key4 到 server :".$hashServer->lookup('key4') . '<br />';
  43. echo "保存 key5 到 server :".$hashServer->lookup('key5') . '<br />';
  44. echo "保存 key6 到 server :".$hashServer->lookup('key6') . '<br />';
  45. echo "保存 key7 到 server :".$hashServer->lookup('key7') . '<br />';
  46. echo "保存 key8 到 server :".$hashServer->lookup('key8') . '<br />';
  47. echo "保存 key9 到 server :".$hashServer->lookup('key9') . '<br />';
  48. echo "保存 key10 到 server :".$hashServer->lookup('key10') . '<br />';
  49. echo '<hr />';
  50. echo "移除一台服务器192.168.1.8<br />";
  51. $hashServer->removeServer('192.168.1.8');
  52. echo "保存 key1 到 server :".$hashServer->lookup('key1') . '<br />';
  53. echo "保存 key2 到 server :".$hashServer->lookup('key2') . '<br />';
  54. echo "保存 key3 到 server :".$hashServer->lookup('key3') . '<br />';
  55. echo "保存 key4 到 server :".$hashServer->lookup('key4') . '<br />';
  56. echo "保存 key5 到 server :".$hashServer->lookup('key5') . '<br />';
  57. echo "保存 key6 到 server :".$hashServer->lookup('key6') . '<br />';
  58. echo "保存 key7 到 server :".$hashServer->lookup('key7') . '<br />';
  59. echo "保存 key8 到 server :".$hashServer->lookup('key8') . '<br />';
  60. echo "保存 key9 到 server :".$hashServer->lookup('key9') . '<br />';
  61. echo "保存 key10 到 server :".$hashServer->lookup('key10') . '<br />';
  62. echo '<hr />';
  63. echo "移除一台服务器192.168.1.2<br />";
  64. $hashServer->removeServer('192.168.1.2');
  65. echo "保存 key1 到 server :".$hashServer->lookup('key1') . '<br />';
  66. echo "保存 key2 到 server :".$hashServer->lookup('key2') . '<br />';
  67. echo "保存 key3 到 server :".$hashServer->lookup('key3') . '<br />';
  68. echo "保存 key4 到 server :".$hashServer->lookup('key4') . '<br />';
  69. echo "保存 key5 到 server :".$hashServer->lookup('key5') . '<br />';
  70. echo "保存 key6 到 server :".$hashServer->lookup('key6') . '<br />';
  71. echo "保存 key7 到 server :".$hashServer->lookup('key7') . '<br />';
  72. echo "保存 key8 到 server :".$hashServer->lookup('key8') . '<br />';
  73. echo "保存 key9 到 server :".$hashServer->lookup('key9') . '<br />';
  74. echo "保存 key10 到 server :".$hashServer->lookup('key10') . '<br />';
  75. echo '<hr />';
  76. echo "增加一台服务器192.168.1.11<br />";
  77. $hashServer->addServer('192.168.1.11');
  78. echo "保存 key1 到 server :".$hashServer->lookup('key1') . '<br />';
  79. echo "保存 key2 到 server :".$hashServer->lookup('key2') . '<br />';
  80. echo "保存 key3 到 server :".$hashServer->lookup('key3') . '<br />';
  81. echo "保存 key4 到 server :".$hashServer->lookup('key4') . '<br />';
  82. echo "保存 key5 到 server :".$hashServer->lookup('key5') . '<br />';
  83. echo "保存 key6 到 server :".$hashServer->lookup('key6') . '<br />';
  84. echo "保存 key7 到 server :".$hashServer->lookup('key7') . '<br />';
  85. echo "保存 key8 到 server :".$hashServer->lookup('key8') . '<br />';
  86. echo "保存 key9 到 server :".$hashServer->lookup('key9') . '<br />';
  87. echo "保存 key10 到 server :".$hashServer->lookup('key10') . '<br />';
  88. echo '<hr />';

运行结果如下

  1. 增加十台服务器192.168.1.1~192.168.1.10
  2. 保存 key1 server :192.168.1.2
  3. 保存 key2 server :192.168.1.1
  4. 保存 key3 server :192.168.1.6
  5. 保存 key4 server :192.168.1.8
  6. 保存 key5 server :192.168.1.9
  7. 保存 key6 server :192.168.1.10
  8. 保存 key7 server :192.168.1.7
  9. 保存 key8 server :192.168.1.4
  10. 保存 key9 server :192.168.1.7
  11. 保存 key10 server :192.168.1.4
  12. 移除一台服务器192.168.1.2
  13. 保存 key1 server :192.168.1.7
  14. 保存 key2 server :192.168.1.1
  15. 保存 key3 server :192.168.1.6
  16. 保存 key4 server :192.168.1.8
  17. 保存 key5 server :192.168.1.9
  18. 保存 key6 server :192.168.1.10
  19. 保存 key7 server :192.168.1.7
  20. 保存 key8 server :192.168.1.4
  21. 保存 key9 server :192.168.1.7
  22. 保存 key10 server :192.168.1.4
  23. 移除一台服务器192.168.1.6
  24. 保存 key1 server :192.168.1.7
  25. 保存 key2 server :192.168.1.1
  26. 保存 key3 server :192.168.1.3
  27. 保存 key4 server :192.168.1.8
  28. 保存 key5 server :192.168.1.9
  29. 保存 key6 server :192.168.1.10
  30. 保存 key7 server :192.168.1.7
  31. 保存 key8 server :192.168.1.4
  32. 保存 key9 server :192.168.1.7
  33. 保存 key10 server :192.168.1.4
  34. 移除一台服务器192.168.1.8
  35. 保存 key1 server :192.168.1.7
  36. 保存 key2 server :192.168.1.1
  37. 保存 key3 server :192.168.1.3
  38. 保存 key4 server :192.168.1.10
  39. 保存 key5 server :192.168.1.9
  40. 保存 key6 server :192.168.1.10
  41. 保存 key7 server :192.168.1.7
  42. 保存 key8 server :192.168.1.4
  43. 保存 key9 server :192.168.1.7
  44. 保存 key10 server :192.168.1.4
  45. 移除一台服务器192.168.1.2
  46. 保存 key1 server :192.168.1.7
  47. 保存 key2 server :192.168.1.1
  48. 保存 key3 server :192.168.1.3
  49. 保存 key4 server :192.168.1.10
  50. 保存 key5 server :192.168.1.9
  51. 保存 key6 server :192.168.1.10
  52. 保存 key7 server :192.168.1.7
  53. 保存 key8 server :192.168.1.4
  54. 保存 key9 server :192.168.1.7
  55. 保存 key10 server :192.168.1.4
  56. 增加一台服务器192.168.1.11
  57. 保存 key1 server :192.168.1.7
  58. 保存 key2 server :192.168.1.1
  59. 保存 key3 server :192.168.1.11
  60. 保存 key4 server :192.168.1.10
  61. 保存 key5 server :192.168.1.9
  62. 保存 key6 server :192.168.1.10
  63. 保存 key7 server :192.168.1.7
  64. 保存 key8 server :192.168.1.4
  65. 保存 key9 server :192.168.1.7
  66. 保存 key10 server :192.168.1.4
可以,看到,使用一致性哈希后,无认是增加服务器还是减少服务器都最大程度的保证了数据的完整性、均匀性.
 

一致性Hash算法(分布式算法)的更多相关文章

  1. 分布式算法(一致性Hash算法)

    一.分布式算法 在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法( ...

  2. 分布式缓存技术memcached学习(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...

  3. 【转载】一致性hash算法释义

    http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Karge ...

  4. 一致性Hash算法及使用场景

    一.问题产生背景      在使用分布式对数据进行存储时,经常会碰到需要新增节点来满足业务快速增长的需求.然而在新增节点时,如果处理不善会导致所有的数据重新分片,这对于某些系统来说可能是灾难性的. 那 ...

  5. 分布式缓存技术memcached学习系列(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到"分布式一致性hash算法"这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前, ...

  6. [转载] 一致性hash算法释义

    转载自http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Ka ...

  7. php一致性hash算法的应用

    阅读这篇博客前首先你需要知道什么是分布式存储以及分布式存储中的数据分片存储的方式有哪些? 分布式存储系统设计(2)—— 数据分片 阅读玩这篇文章后你会知道分布式存储的最优方案是使用 一致性hash算法 ...

  8. 分布式一致性hash算法

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

  9. 一致性Hash算法(Consistent Hash)

    分布式算法 在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Re ...

随机推荐

  1. mysql ERROR 1451 (23000)

    问题描述:报错如下:ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint提示有外键约束, ...

  2. spring BeanWrapperImpl方便的嵌套属性(list)操作

    beans 包主要提供了接口和类用于处理java beans.     其中最主要的接口是BeanWrapper:     Spring 的中心接口,用于访问javabeans 的低层操作.默认实现为 ...

  3. 20个Linux防火墙应用技巧

    转载 1.显示防火墙的状态 以root权限运行下面的命令: # iptables -L -n -v 参数说明: -L:列出规则. -v:显示详细信息.此选项会显示接口名称.规则选项和TOS掩码,以及封 ...

  4. _ZNote_Qt_添加图标方法

    简单来说就两步: 将icns图标添加入资源文件,例如picture.icns .pro文件中添加 (图标) ICON = picture.icns 程序中添加(程序窗口上显示) setWindowIc ...

  5. 制作系统U盘

    没有任何宣传软件成分昂,我就是这做的. 1.在百度搜索上搜索“通用PE大师”,点开了这个网站http://up.6615261.cn/index.html,打开之后如下图,下载这个二合一版本 2.在电 ...

  6. 我所理解的HTTP协议

    前言 对于HTTP协议,想必大家都不陌生,在工作中经常用到,特别是针对移动端和前端开发人员来说,要获取服务端数据,基本走的网络请求都是基于HTTP协议,特别是RESTFUL + JSON 这种搭配特别 ...

  7. python爬虫学习之日志记录模块

    这次的代码就是一个日志记录模块,代码很容易懂,注释很详细,也不需要安装什么库.提供的功能是日志可以显示在屏幕上并且保存在日志文件中.调用的方式也很简单,测试代码里面有. 源代码: #encoding= ...

  8. Vue过渡mode属性踩坑

    近期学习Vue的过渡效果的时候,mode属性的"in-out"."out-in"设置了不起作用,官网上的例子让我看了有点迷,问题解决后以此文记录之. 首先我们看 ...

  9. 10. 面向holder编程、自动轮询

    没有看过上一篇文章的话,建议先去阅读GooglePlay:9.代码抽取(adapter) 项目框架的搭建: 以后每个view都是holder BaseHolder public abstract cl ...

  10. 《http权威指南》读书笔记13

    概述 最近对http很感兴趣,于是开始看<http权威指南>.别人都说这本书有点老了,而且内容太多.我个人觉得这本书写的太好了,非常长知识,让你知道关于http的很多概念,不仅告诉你怎么做 ...