一致性Hash算法

关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中”一致性Hash算法”部分,对于为什么要使用一致性Hash算法、一致性Hash算法的算法原理做了详细的解读。

算法的具体原理这里再次贴上:

先构造一个长度为2^32的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 2^32-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 2^32-1]),接着在Hash环上顺时针查找距离这个Key值的Hash值最近的服务器节点,完成Key到服务器的映射查找。

这种算法解决了普通余数Hash算法伸缩性差的问题,可以保证在上线、下线服务器的情况下尽量有多的请求命中原来路由到的服务器。

当然,万事不可能十全十美,一致性Hash算法比普通的余数Hash算法更具有伸缩性,但是同时其算法实现也更为复杂,本文就来研究一下,如何利用Java代码实现一致性Hash算法。在开始之前,先对一致性Hash算法中的几个核心问题进行一些探究。

数据结构的选取

一致性Hash算法最先要考虑的一个问题是:构造出一个长度为2^32的整数环,根据节点名称的Hash值将服务器节点放置在这个Hash环上。

那么,整数环应该使用何种数据结构,才能使得运行时的时间复杂度最低?首先说明一点,关于时间复杂度,常见的时间复杂度与时间效率的关系有如下的经验规则:

O(1) < O(log2N) < O(n) < O(N * logN) < O(N^2) < O(N^3) < 2^N < 3^N < N!

一般来说,前四个效率比较高,中间两个差强人意,后三个比较差(只要N比较大,这个算法就动不了了)。OK,继续前面的话题,应该如何选取数据结构,我认为有以下几种可行的解决方案。

1、解决方案一:排序+List

我想到的第一种思路是:算出所有待加入数据结构的节点名称的Hash值放入一个数组中,然后使用某种排序算法将其从小到大进行排序,最后将排序后的数据放入List中,采用List而不是数组是为了结点的扩展考虑。

之后,待路由的结点,只需要在List中找到第一个Hash值比它大的服务器节点就可以了,比如服务器节点的Hash值是[0,2,4,6,8,10],带路由的结点是7,只需要找到第一个比7大的整数,也就是8,就是我们最终需要路由过去的服务器节点。

如果暂时不考虑前面的排序,那么这种解决方案的时间复杂度:

(1)最好的情况是第一次就找到,时间复杂度为O(1)

(2)最坏的情况是最后一次才找到,时间复杂度为O(N)

平均下来时间复杂度为O(0.5N+0.5),忽略首项系数和常数,时间复杂度为O(N)。

但是如果考虑到之前的排序,我在网上找了张图,提供了各种排序算法的时间复杂度:

2、解决方案二:遍历+List

既然排序操作比较耗性能,那么能不能不排序?可以的,所以进一步的,有了第二种解决方案。

解决方案使用List不变,不过可以采用遍历的方式:

(1)服务器节点不排序,其Hash值全部直接放入一个List中

(2)带路由的节点,算出其Hash值,由于指明了”顺时针”,因此遍历List,比待路由的节点Hash值大的算出差值并记录,比待路由节点Hash值小的忽略

(3)算出所有的差值之后,最小的那个,就是最终需要路由过去的节点

在这个算法中,看一下时间复杂度:

1、最好情况是只有一个服务器节点的Hash值大于带路由结点的Hash值,其时间复杂度是O(N)+O(1)=O(N+1),忽略常数项,即O(N)

2、最坏情况是所有服务器节点的Hash值都大于带路由结点的Hash值,其时间复杂度是O(N)+O(N)=O(2N),忽略首项系数,即O(N)

所以,总的时间复杂度就是O(N)。其实算法还能更改进一些:给一个位置变量X,如果新的差值比原差值小,X替换为新的位置,否则X不变。这样遍历就减少了一轮,不过经过改进后的算法时间复杂度仍为O(N)。

总而言之,这个解决方案和解决方案一相比,总体来看,似乎更好了一些。

3、解决方案三:二叉查找树

抛开List这种数据结构,另一种数据结构则是使用二叉查找树。

当然我们不能简单地使用二叉查找树,因为可能出现不平衡的情况。平衡二叉查找树有AVL树、红黑树等,这里使用红黑树,选用红黑树的原因有两点:

1、红黑树主要的作用是用于存储有序的数据,这其实和第一种解决方案的思路又不谋而合了,但是它的效率非常高

2、JDK里面提供了红黑树的代码实现TreeMap和TreeSet

另外,以TreeMap为例,TreeMap本身提供了一个tailMap(K fromKey)方法,支持从红黑树中查找比fromKey大的值的集合,但并不需要遍历整个数据结构。

使用红黑树,可以使得查找的时间复杂度降低为O(logN),比上面两种解决方案,效率大大提升。

为了验证这个说法,我做了一次测试,从大量数据中查找第一个大于其中间值的那个数据,比如10000数据就找第一个大于5000的数据(模拟平均的情况)。看一下O(N)时间复杂度和O(logN)时间复杂度运行效率的对比:

因为再大就内存溢出了,所以只测试到4000000数据。可以看到,数据查找的效率,TreeMap是完胜的,其实再增大数据测试也是一样的,红黑树的数据结构决定了任何一个大于N的最小数据,它都只需要几次至几十次查找就可以查到。

当然,明确一点,有利必有弊,根据我另外一次测试得到的结论是,为了维护红黑树,数据插入效率TreeMap在三种数据结构里面是最差的,且插入要慢上5~10倍。

Hash值重新计算

服务器节点我们肯定用字符串来表示,比如”192.168.1.1″、”192.168.1.2″,根据字符串得到其Hash值,那么另外一个重要的问题就是Hash值要重新计算,这个问题是我在测试String的hashCode()方法的时候发现的,不妨来看一下为什么要重新计算Hash值:

/*** String的hashCode()方法运算结果查看* @author 哓哓**/public class StringHashCodeTest { public static void main(String[] args) { System.out.println("192.168.0.0:111的哈希值:" + "192.168.0.0:1111".hashCode()); System.out.println("192.168.0.1:111的哈希值:" + "192.168.0.1:1111".hashCode()); System.out.println("192.168.0.2:111的哈希值:" + "192.168.0.2:1111".hashCode()); System.out.println("192.168.0.3:111的哈希值:" + "192.168.0.3:1111".hashCode()); System.out.println("192.168.0.4:111的哈希值:" + "192.168.0.4:1111".hashCode()); }}

我们在做集群的时候,集群点的IP以这种连续的形式存在是很正常的。看一下运行结果为:

192.168.0.0:111的哈希值:1845870087192.168.0.1:111的哈希值:1874499238192.168.0.2:111的哈希值:1903128389192.168.0.3:111的哈希值:1931757540192.168.0.4:111的哈希值:1960386691

这个就问题大了,[0,2^32-1]的区间之中,5个HashCode值却只分布在这么小小的一个区间,什么概念?[0,2^32-1]中有4294967296个数字,而我们的区间只有122516605,从概率学上讲这将导致97%待路由的服务器都被路由到”192.168.0.1″这个集群点上,简直是糟糕透了!

另外还有一个不好的地方:规定的区间是非负数,String的hashCode()方法却会产生负数(不信用”192.168.1.0:1111″试试看就知道了)。不过这个问题好解决,取绝对值就是一种解决的办法。

综上,String重写的hashCode()方法在一致性Hash算法中没有任何实用价值,得找个算法重新计算HashCode。这种重新计算Hash值的算法有很多,比如CRC32_HASH、FNV1_32_HASH、KETAMA_HASH等,其中KETAMA_HASH是默认的MemCache推荐的一致性Hash算法,用别的Hash算法也可以,比如FNV1_32_HASH算法的计算效率就会高一些。

一致性Hash算法实现版本1:不带虚拟节点

使用一致性Hash算法,尽管增强了系统的伸缩性,但是也有可能导致负载分布不均匀,解决办法就是使用虚拟节点代替真实节点,第一个代码版本,先来个简单的,不带虚拟节点。

下面来看一下不带虚拟节点的一致性Hash算法的Java代码实现:

/** * 不带虚拟结点的一致性Hash算法 * @author 哓哓 * */public class ConsistentHashWithoutVN {​ /** * 待加入Hash环的服务器列表 */ private static String[] servers = { "192.168.0.0:111

一致性hash算法_(图文案例)一致性哈希算法详解 一点课堂(多岸教育)相关推荐

  1. 文本处理算法_关键词提取和文本摘要算法TextRank详解及实战

    关键词提取和文本摘要算法TextRank详解及实战 写在前面 最近一直没有更新文章,实在惭愧.伴随着小老弟的职业方向由风控转向了NLP,后面的文章也会集中在NLP领域,希望大家能够继续支持~ 导读 本 ...

  2. Hibernate_1_配置文件详解_基础案例_Hibernate工具类_API详解_持久化类编写规则

    Hibernate( ORM框架 ) Hibernate是一个数据持久化层的ORM框架. 它通过JavaBean, 数据库中的表与自身的映射关系达到表中数据的增删改查 特性 1.对JDBC访问数据库的 ...

  3. Python_机器学习_算法_第1章_K-近邻算法

    Python_机器学习_算法_第1章_K-近邻算法 文章目录 Python_机器学习_算法_第1章_K-近邻算法 K-近邻算法 学习目标 1.1 K-近邻算法简介 学习目标 1 什么是K-近邻算法 1 ...

  4. 算法学习笔记13:哈希算法

    哈希算法(上):如何防止数据库中的用户信息被脱库 什么是哈希算法 应用一:安全加密 应用二:唯一标识 应用三:数据校验 应用四:散列函数 解答开篇 哈希算法(下):哈希算法在分布式系统中有哪些应用 应 ...

  5. JDBC学习笔记02【ResultSet类详解、JDBC登录案例练习、PreparedStatement类详解】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  6. python符号格式化设置区间_Python 数值区间处理_对interval 库的快速入门详解

    使用 Python 进行数据处理的时候,常常会遇到判断一个数是否在一个区间内的操作.我们可以使用 if else 进行判断,但是,既然使用了 Python,那我们当然是想找一下有没有现成的轮子可以用. ...

  7. 微信公众号图文消息添加word附件教程详解

    微信公众号图文消息添加word附件教程详解 我们都知道创建一个微信公众号,在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序"微附件"进行在 ...

  8. 视频教程-2020年软考网络规划设计师案例分析历年真题详解软考视频教程-软考

    2020年软考网络规划设计师案例分析历年真题详解软考视频教程 10年以上软考培训经验,线下培训学员过万人.培训过的课程有:网络规划设计师.网络工程师.信 息系统项目管理师.系统集成项目管理师.信息安全 ...

  9. 朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解

    相关文章 K近邻算法和KD树详细介绍及其原理详解 朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解 决策树算法和CART决策树算法详细介绍及其原理详解 线性回归算法和逻辑斯谛回归算法详细介绍及其原理详 ...

最新文章

  1. 操作系统的msxml组件版本过低_Zabbix 5.0 LTS 版本安装
  2. istio 和 kong_如何启动和运行Istio
  3. 演示:外部全局地址与外部局部地址的使用案例
  4. python异步回调实现原理_快速理解Python异步编程的基本原理
  5. 英文操作系统下WebBrowser控件无法显示本地页面的解决方法
  6. System学习笔记006---Windows退出telnet 命令的正确姿势
  7. 将node.js程序作为服务,并在windows下开机自动启动(使用forever)
  8. 爆米花现象_芯片爆米花现象以及解决办法
  9. windows下安装foremost和binwalk(以及两个软件的安装包)
  10. SSH使用教程( Bitvise Tunnelier+Chrome+Proxy Switchy)
  11. 地狱已满 服务器无响应,steam地狱已满怎么联网
  12. 深度学习结合SLAM的研究思路/成果整理之(二)语义SLAM 端到端
  13. 用Wireshark下载微信小程序里的视频【图文教程】
  14. 2018n年全国计算机考试,2018ncre全国计算机等级考试报名系统
  15. python中如何判断大小写_用python如何判断字符的大小写
  16. windows10许可证即将过期怎么办_Windows 7 时代即将终结!
  17. 案例:5秒后关闭广告 定时器
  18. 面试题2021.7.5-mysql
  19. 2021-12-07(JZ83 剪绳子(进阶版))
  20. 芋道源码的周八(2018.04.08)

热门文章

  1. FFMPEG的详细资料可以在它的官方网站上找到
  2. 公司技术管理角度看C++游戏程序员发展
  3. Docker 环境下如何 安装 Zookeeper
  4. DUBBO 使用问题记录
  5. python-flask-1
  6. jquery评分效果Rating精华版
  7. 论文笔记 Aggregated Residual Transformations for Deep Neural Networks
  8. Windows - Windows下安装MSI程序遇到2503和2502错误
  9. Tomcat 配置详解/优化方案
  10. signals系列之一——基本用法