一起进阶学习JAVA:一致性Hash算法
Hash算法,⽐如说在安全加密领域MD5、SHA等加密算法,在数据存储和查找⽅⾯有Hash表等, 以上都应用到了Hash算法。
为什么需要使用Hash?
Hash算法较多的应⽤在数据存储和查找领域,最经典的就是Hash表,它的查询效率⾮常之⾼,其中的
哈希算法如果设计的比较好的话,那么Hash表的数据查询时间复杂度可以接近于O(1)
需求:提供⼀组数据 3,6,7,9,2,1,0,8,10对这组数据进⾏存储,然后随便给定⼀个数n,请你判断n是否存在于刚才的数据集中?
- 顺序寻值法
以顺序方式在数据组中循环遍历比对,这种方法比较原始,效率较低 - 二分查找法
排序后折半查找,相比顺序寻值法来说效率有所提升 - 直接寻址法
定义⼀个数组,数组⻓度⼤于等于数据集⻓度,数据0就存在下标为0的数组空间中,以此类推。这种方法将值和数组下标进行绑定,在少量数据中效率很高,但是对于值较大且值范围分布较散的情况下及其浪费内存空间。
以上三种方法都有各自的缺点,要么空间浪费要么效率较低。
换⼀种设计,如果数据是3,5,7,12306,⼀共4个数据,我们开辟任意个空间,⽐如5个,那
么具体数据存储到哪个位置呢,我们可以对数据进⾏求模(对空间位置数5),根据求模余数确定存储
位置的下标,⽐如3%5=3,就可以把3这个数据放到下标为3的位置上,12306%5=1,就把12306这个
数据存储到下标为1的位置上
上⾯对数据求模 (数据%空间位置数) 他就是⼀个hash算法,只不过这是⼀种⽐较普通⼜简单的hash
算法,这种构造Hash算法的⽅式叫做除留余数法
如果数据是1,6,7,8,把这4个数据存储到上⾯的数组中
在此基础上采⽤开放寻址法
- 开放寻址法:1放进去了,6再来的时候,向前或者向后找空闲位置存放,不好的地⽅,如果数组⻓度定义好了⽐如10,⻓度不能扩展,来了11个数据,不管Hash冲突不冲突,肯定存不下这么多数据
- 拉链法:数据⻓度定义好了,怎么存储更多内容呢,算好Hash值,在数组元素存储位置放了⼀个链表
Hash算法的应用场景
Hash算法在很多分布式集群产品中都有应⽤,⽐如分布式集群架构Redis、Hadoop、ElasticSearch,
Mysql分库分表,Nginx负载均衡等
主要的应用场景有两个:
- 请求的负载均衡(⽐如nginx的ip_hash策略)
在请求的负载均衡中,我们可以通过对ip地址进行hash计算也可以对sessionid做hash计算,hash值与服务器数量等进行取模运算,得到的值就可以路由到具体请求的服务器上 - 分布式存储
在分布式存储中,针对key进⾏hash处理,hash(key1)%3=index, 使⽤余数index锁定存储的具体服务器节点
普通Hash算法存在的问题
普通Hash算法存在⼀个问题,以ip_hash为例,假定下载⽤户ip固定没有发⽣改变,现在tomcat3出现
了问题,down机了,服务器数量由3个变为了2个,之前所有的求模都需要重新计算。那么在真实生产环境中我们的客户端会有很多个,服务器也会有很多个,这样当缩容和扩容的情况下影响的范围是相当广泛的。
一致性Hash算法
⼀致性哈希算法思路如下:
⾸先有⼀条直线,直线开头和结尾分别定为为1和2的32次⽅减1,这相当于⼀个地址,对于这样⼀条
线,弯过来构成⼀个圆环形成闭环,这样的⼀个圆环称为hash环。我们把服务器的ip或者主机名求
hash值然后对应到hash环上,那么针对客户端⽤户,也根据它的ip进⾏hash求值,对应到环上某个位
置,然后如何确定⼀个客户端路由到哪个服务器处理呢?按照顺时针⽅向找最近的服务器节点
假如将服务器3下线,服务器3下线后,原来路由到3的客户端重新路由到服务器4,对于其他客户端没有
影响只是这⼀⼩部分受影响(请求的迁移达到了最⼩,这样的算法对分布式集群来说⾮常合适的,避免
了⼤量请求迁移 )
增加服务器5之后,原来路由到3的部分客户端路由到新增服务器5上,对于其他客户端没有影响只是这
⼀⼩部分受影响(请求的迁移达到了最⼩,这样的算法对分布式集群来说⾮常合适的,避免了⼤量请求
迁移 )
如前所述,每⼀台服务器负责⼀段,⼀致性哈希算法对于节点的增减都只需重定位环空间中的⼀⼩
部分数据,具有较好的容错性和可扩展性。
但是,⼀致性哈希算法在服务节点太少时,容易因为节点分部不均匀⽽造成数据倾斜问题。例如系统中
只有两台服务器,其环分布如下,节点2只能负责⾮常⼩的⼀段,⼤量的客户端
请求落在了节点1上,这就是数据(请求)倾斜问题
虚拟节点机制
为了解决这种数据倾斜问题,⼀致性哈希算法引⼊了虚拟节点机制,即对每⼀个服务节点计算多个
哈希,每个计算结果位置都放置⼀个此服务节点,称为虚拟节点。
具体做法可以在服务器ip或主机名的后⾯增加编号来实现。⽐如,可以为每台服务器计算三个虚拟节
点,于是可以分别计算 “节点1的ip#1”、“节点1的ip#2”、“节点1的ip#3”、“节点2的ip#1”、“节点2的
ip#2”、“节点2的ip#3”的哈希值,于是形成六个虚拟节点,当客户端被路由到虚拟节点的时候其实是被
路由到该虚拟节点所对应的真实节点。