本文为拉勾课程的学习笔记
为什么需要使用hash
Hash算法较多的应⽤在数据存储和查找领域,最经典的就是Hash表,它的查询效率⾮常之⾼,其中的 哈希算法如果设计的⽐较ok的话,那么Hash表的数据查询时间复杂度可以接近于O(1)。
举个🌰
提供一组数据 1,5,7,6,3,4,8,对这组数据进行存储,然后随便给定一个数n,判断n是否存在在提供的数据中。
1、可以存在集合或数组中,然后通过循环判断是否存在,时间复杂度O(n)
2、存在集合或数组中,然后二分查找,时间复杂度O(logN)
3、定义一个数组,下标对应数值的大小,比如:1就存到下标为1的位置,5就存到下标为5的位置。这样去取的话,时间复杂度为O(1). 这种方式叫做“直接寻址法” 存在的问题:浪费空间,数据重复无法存储。
4、换一种设计,如果是数据3,5,7,12306,一共四个数据,我们用数值和数组大小取模,来得到下标。比如,假定数组大小为5,则3%5=5,12306%5=1,则,可以按下面的方式存储
这就是一种普通的hash算法
但是这个会出现hash冲突,比如1,6,7,8,这四个数要存到上面的数组中
解决hash冲突有两种方式
1、开放寻址法:1放进去了,6再来的时候,向前或者向后找空闲位置存放,不好的地⽅,如果数组⻓度定义好了⽐如10,⻓度不能扩展,来了11个数据,不管Hash冲突不冲突,肯定存不下这么多数据
2、拉链法:数据⻓度定义好了,怎么存储更多内容呢,算好Hash值,在数组元素存储位置放了⼀个链表
拉链法是常用的一种方式,hashmap底层就是用的这个。 但是拉链法也可能效率很低,比如下面
所以hash表的查询效率取决于hash算法。
hash算法的应用场景
hash算法在分布式集群架构中的应用场景
Hash算法在很多分布式集群产品中都有应⽤,⽐如分布式集群架构Redis、Hadoop、ElasticSearch, Mysql分库分表,Nginx负载均衡等
主要的应⽤场景归纳起来两个
请求的负载均衡(⽐如nginx的ip_hash策略)
Nginx的IP_hash策略可以在客户端ip不变的情况下,将其发出的请求始终路由到同⼀个⽬标服务
器上,实现会话粘滞,避免处理session共享问题
如果没有IP_hash策略,那么如何实现会话粘滞?
可以维护⼀张映射表,存储客户端IP或者sessionid与具体⽬标服务器的映射关系
<ip,tomcat1>
缺点
1)那么,在客户端很多的情况下,映射表⾮常⼤,浪费内存空间
2)客户端上下线,⽬标服务器上下线,都会导致重新维护映射表,映射表维护成本很⼤
如果使⽤哈希算法,事情就简单很多,我们可以对ip地址或者sessionid进⾏计算哈希值,哈希值与服务器数量进⾏取模运算,得到的值就是当前请求应该被路由到的服务器编号,如此,同⼀个客户端ip发送过来的请求就可以路由到同⼀个⽬标服务器,实现会话粘滞。
分布式存储
以分布式内存数据库Redis为例,集群中有redis1,redis2,redis3 三台Redis服务器
那么,在进⾏数据存储时,<key1,value1>数据存储到哪个服务器当中呢?针对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的请求就会路由到服务器4,其他服务没影响。
增加服务器5之后,原来路由到3的部分客户端路由到新增服务器5上,对于其他客户端没有影响只是这⼀⼩部分受影响
但是,在服务节点太少时容易造成数据倾斜。 比如只有1,2两台服务器,在环上的分布如下
这样大部分请求将会落到服务器1,这就是数据倾斜问题
如何解决?
可以引⼊了虚拟节点机制,即对每⼀个服务节点计算多个哈希,每个计算结果位置都放置⼀个此服务节点,称为虚拟节点。
具体做法可以在服务器ip或主机名的后⾯增加编号来实现。⽐如,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “节点1的ip#1”、“节点1的ip#2”、“节点1的ip#3”、“节点2的ip#1”、“节点2的ip#2”、“节点2的ip#3”的哈希值,于是形成六个虚拟节点,当客户端被路由到虚拟节点的时候其实是被路由到该虚拟节点所对应的真实节点
为什么是2的32次方减一?
因为一致性hash算法是来做服务器的负载均衡,而服务器的IP地址是32位,所以是2^32-1次方的数值空间