作者简介:

华哥

10年+后端开发工作经验,

主要分享:关于java体系的知识,如:java基础知识/数据结算/算法,Spring/MyBatis/Netty源码分析,高并发/高性能/分布式/微服务架构的原理,JVM性能优化等。


一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用。

但现在一致性hash算法在分布式系统中也得到了广泛应用,研究过memcached缓存数据库的人都知道,memcached服务器端本身不提供分布式cache的一致性,而是由客户端来提供,具体在计算一致性hash时采用如下步骤:

  1. 首先求出memcached服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上。
  2. 然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
  3. 然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上。

从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率,但Consistent Hashing中,只有在圆(continuum)上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响,如下图所示:

一致性Hash性质

考虑到分布式系统每个节点都有可能失效,并且新的节点很可能动态的增加进来,如何保证当系统的节点数目发生变化时仍然能够对外提供良好的服务,这是值得考虑的,尤其实在设计分布式缓存系统时,如果某台服务器失效,对于整个系统来说如果不采用合适的算法来保证一致性,那么缓存于系统中的所有数据都可能会失效(即由于系统节点数目变少,客户端在请求某一对象时需要重新计算其hash值(通常与系统中的节点数目有关),由于hash值已经改变,所以很可能找不到保存该对象的服务器节点),因此一致性hash就显得至关重要,良好的分布式cahce系统中的一致性hash算法应该满足以下几个方面:

  • 平衡性(Balance)

平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。

  • 单调性(Monotonicity)

单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。简单的哈希算法往往不能满足单调性的要求,如最简单的线性哈希:x = (ax + b) mod (P),在上式中,P表示全部缓冲的大小。不难看出,当缓冲大小发生变化时(从P1到P2),原来所有的哈希结果均会发生变化,从而不满足单调性的要求。哈希结果的变化意味着当缓冲空间发生变化时,所有的映射关系需要在系统内全部更新。而在P2P系统内,缓冲的变化等价于Peer加入或退出系统,这一情况在P2P系统中会频繁发生,因此会带来极大计算和传输负荷。单调性就是要求哈希算法能够应对这种情况。

  • 分散性(Spread)

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

  • 负载(Load)

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

  • 平滑性(Smoothness)

平滑性是指缓存服务器的数目平滑改变和缓存对象的平滑改变是一致的。

原理

基本概念

一致性哈希算法(Consistent Hashing)最早在论文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》中被提出。简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形),整个哈希空间环如下:

整个空间按顺时针方向组织。0和232-1在零点中方向重合。

下一步将各个服务器使用Hash进行一个哈希,具体可以选择服务器的ip或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置,这里假设将上文中四台服务器使用ip地址哈希后在环空间的位置如下:

接下来使用如下算法定位数据访问到相应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。

例如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:

根据一致性哈希算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。

下面分析一致性哈希算法的容错性和可扩展性。现假设Node C不幸宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性哈希算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。

下面考虑另外一种情况,如果在系统中增加一台服务器Node X,如下图所示:

此时对象Object A、B、D不受影响,只有对象C需要重定位到新的Node X 。一般的,在一致性哈希算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它数据也不会受到影响。

综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

另外,一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题。例如系统中只有两台服务器,其环分布如下,

此时必然造成大量数据集中到Node A上,而只有极少量会定位到Node B上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器ip或主机名的后面增加编号来实现。例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:

同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点的数据均定位到Node A上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。

JAVA代码实现

package org.java.base.hash;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;public class ConsistentHash<T> {private final int numberOfReplicas;// 节点的复制因子,实际节点个数 * numberOfReplicas =// 虚拟节点个数private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();// 存储虚拟节点的hash值到真实节点的映射public ConsistentHash( int numberOfReplicas,Collection<T> nodes) {this.numberOfReplicas = numberOfReplicas;for (T node : nodes){add(node);}}public void add(T node) {for (int i = 0; i < numberOfReplicas; i++){// 对于一个实际机器节点 node, 对应 numberOfReplicas 个虚拟节点/** 不同的虚拟节点(i不同)有不同的hash值,但都对应同一个实际机器node* 虚拟node一般是均衡分布在环上的,数据存储在顺时针方向的虚拟node上*/String nodestr =node.toString() + i;int hashcode =nodestr.hashCode();System.out.println("hashcode:"+hashcode);circle.put(hashcode, node);}}public void remove(T node) {for (int i = 0; i < numberOfReplicas; i++)circle.remove((node.toString() + i).hashCode());}/** 获得一个最近的顺时针节点,根据给定的key 取Hash* 然后再取得顺时针方向上最近的一个虚拟节点对应的实际节点* 再从实际节点中取得 数据*/public T get(Object key) {if (circle.isEmpty())return null;int hash = key.hashCode();// node 用String来表示,获得node在哈希环中的hashCodeSystem.out.println("hashcode----->:"+hash);if (!circle.containsKey(hash)) {//数据映射在两台虚拟机器所在环之间,就需要按顺时针方向寻找机器SortedMap<Integer, T> tailMap = circle.tailMap(hash);hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();}return circle.get(hash);}public long getSize() {return circle.size();}/** 查看表示整个哈希环中各个虚拟节点位置*/public void testBalance(){Set<Integer> sets = circle.keySet();//获得TreeMap中所有的KeySortedSet<Integer> sortedSets= new TreeSet<Integer>(sets);//将获得的Key集合排序for(Integer hashCode : sortedSets){System.out.println(hashCode);}System.out.println("----each location 's distance are follows: ----");/** 查看相邻两个hashCode的差值*/Iterator<Integer> it = sortedSets.iterator();Iterator<Integer> it2 = sortedSets.iterator();if(it2.hasNext())it2.next();long keyPre, keyAfter;while(it.hasNext() && it2.hasNext()){keyPre = it.next();keyAfter = it2.next();System.out.println(keyAfter - keyPre);}}public static void main(String[] args) {Set<String> nodes = new HashSet<String>();nodes.add("A");nodes.add("B");nodes.add("C");ConsistentHash<String> consistentHash = new ConsistentHash<String>(2, nodes);consistentHash.add("D");System.out.println("hash circle size: " + consistentHash.getSize());System.out.println("location of each node are follows: ");consistentHash.testBalance();String node =consistentHash.get("apple");System.out.println("node----------->:"+node);}}


作者简介:

华哥

10年+后端开发工作经验,

主要分享:关于java体系的知识,如:java基础知识/数据结算/算法,Spring/MyBatis/Netty源码分析,高并发/高性能/分布式/微服务架构的原理,JVM性能优化等。

更多的学习资料,请看这里:

http://www.jnshu.com/login/1/36856070?source=zhihu-article-huage

的一致性哈希_五分钟看懂一致性哈希算法相关推荐

  1. 智能网联技术 英文_五分钟看懂通用V2V智能网联技术

    随着汽车科技的不断发展,汽车也变得越来越智能了.一系列的辅助驾驶系统.主动安全系统的涌现为驾驶者驾驶车辆提供了安全保障.人与车的互动我们是司空见惯了,而如今出现一种新型V2V通讯技术能够让相互靠近的汽 ...

  2. 三相逆变器双pi控制器参数如何调节_一分钟看懂维也纳三相整流器

    欢迎加入技术交流QQ群(2000人):电力电子技术与新能源 1105621549 高可靠新能源行业顶尖自媒体 在这里有电力电子.新能源干货.行业发展趋势分析.最新产品介绍.众多技术达人与您分享经验,欢 ...

  3. 三分钟看懂一致性哈希算法

    一致性哈希算法,作为分布式计算的数据分配参考,比传统的取模,划段都好很多. 在电信计费中,可以作为多台消息接口机和在线计费主机的分配算法,根据session_id来分配,这样当计费主机动态伸缩的时候, ...

  4. aps是什么意思_三分钟看懂ERP、MES、APS系统的关联和区别

    ERP系统在于对整个企业与集团企业间的全面管控,APS系统是生产规划及排程系统,MES系统重点在于车间现场的管理,下面用最简单方式讲讲ERP系统.APS系统和MES系统之间的区别与联系. ERP:企业 ...

  5. 五分钟看懂抓包神技:DPDK

    我是一个网络监控软件,我被开发出来的使命就是监控网络中进进出出的所有通信流量. 一直以来,我的工作都非常的出色,但是随着我监控的网络越来越庞大,网络中的通信流量也变得越来越多,我开始有些忙不过来了,逐 ...

  6. 漫画:五分钟看懂车联网

    戳蓝字"CSDN云计算"关注我们哦! 福利 扫描添加小编微信,备注"姓名+公司职位",加入[云计算学习交流群],和志同道合的朋友们共同打卡学习! 推荐阅读: 华 ...

  7. 五分钟看懂同城小程序是什么,能做什么!

    我们都知道,微信小程序是基于微信这样一个社交平台运行的轻应用,不需要用户下载任何软件,只需要有微信就能够直接使用,非常方便. 微趋道,就是小程序 对于普通商家来说,小程序可谓是营销利器,开发成本低,运 ...

  8. 五分钟看懂python函数(同Java方法作比较)

    今天来学习python函数,对于有java基础的我来说那岂不是手到擒来 既然已经有java基础了,那么首先我想到的就是两个问题 python如何定义函数 python如何调用函数 python中如何定 ...

  9. 什么是嵌入式?你眼中的嵌入式是怎么样的?怎么学嵌入式?教你五分钟看懂嵌入式。

    你眼中的嵌入式是怎么样的? 嵌入式的定义 从技术上来区分嵌入式 嵌入式硬件工程师必备技能 嵌入式驱动工程师必备技能 嵌入式软件开发工程师必备技能 嵌入式系统开发工程师必备技能 总结 嵌入式的定义 IE ...

最新文章

  1. SpringBoot之日志
  2. zabbix监控防火墙和交换机
  3. 流水线上的思考——异步程序开发模型(2)
  4. ★_当第二次检测时我的65047777
  5. Python系统性能信息模块
  6. python 鸭子类型_Python中的鸭子输入是什么?
  7. java setmodal 不管用_java – 无法动态设置setVisibility()参数
  8. 整合nagios+cacti遇到问题及解决办法
  9. keil如何下载STM32芯片F1/F4固件库
  10. 1067 Sort with Swap(0, i)
  11. Gossip in Cassandra
  12. 关于日记app的思考
  13. C++函数的定义与使用
  14. 操作系统知识点(考试版)
  15. word2vec源码详解
  16. 深度学习(一)最通俗易懂的 Transformer
  17. 微软产品关于首字母大写的解决方案
  18. 什么是 web API? DOM和BOM
  19. CentOS7 mysql8.0 国内镜像源安装
  20. EditText相关设置

热门文章

  1. eclipse设置工作空间编码为默认utf-8
  2. QT C1041错误
  3. 关于iOS 'The sandbox is not sync with the Podfile.lock'问题解决方法
  4. Android Task 相关
  5. Python 打印嵌套list中每个数据(遍历列表)
  6. jquery获取radio值
  7. 【2010】asp.net GridView分页的实现
  8. Python之数据分析(numpy中的多项式拟合)
  9. 果园机器人反思稿_《果园机器人》教学反思
  10. VUE-用到的样式左右(transform,translate,padding)