一致性哈希是什么? 使用场景? 解决了什么问题?
如何分配请求
大多数网站背后肯定不是只有一台服务器提供服务,因为单机的并发量和数据量都是有限的,所以都会用多台服务器构成集群来对外提供服务。
但是问题来了,现在有那么多个节点(后面统称服务器为节点,因为少一个字),要如何分配客户端的请求呢?
其实这个问题就是「负载均衡问题」。解决负载均衡问题的算法很多,不同的负载均衡算法,对应的就是不同的分配策略,适应的业务场景也不同。
最简单的方式,引入一个中间的负载均衡层,让它将外界的请求「轮流」的转发给内部的集群。比如集群有 3 个节点,外界请求有 3 个,那么每个节点都会处理 1 个请求,达到了分配请求的目的。
考虑到每个节点的硬件配置有所区别,我们可以引入权重值,将硬件配置更好的节点的权重值设高,然后根据各个节点的权重值,按照一定比重分配在不同的节点上,让硬件配置更好的节点承担更多的请求,这种算法叫做加权轮询。
加权轮询算法使用场景是建立在每个节点存储的数据都是相同的前提。所以,每次读数据的请求,访问任意一个节点都能得到结果。
但是,加权轮询算法是无法应对「分布式系统」的,因为分布式系统中,每个节点存储的数据是不同的。
当我们想提高系统的容量,就会将数据水平切分到不同的节点来存储,也就是将数据分布到了不同的节点。比如一个分布式 KV(key-valu) 缓存系统,某个 key 应该到哪个或者哪些节点上获得,应该是确定的,不是说任意访问一个节点都可以得到缓存结果的。
因此,我们要想一个能应对分布式系统的负载均衡算法。
使用哈希算法有什么问题?
有的同学可能很快就想到了:哈希算法。因为对同一个关键字进行哈希计算,每次计算都是相同的值,这样就可以将某个 key 确定到一个节点了,可以满足分布式系统的负载均衡需求。
哈希算法最简单的做法就是进行取模运算,比如分布式系统中有 3 个节点,基于 hash(key) % 3 公式对数据进行了映射。
如果经过上面这个公式计算后得到的值是 0,就说明该 key 需要去第一个节点获取。
但是有一个很致命的问题,如果节点数量发生了变化,也就是在对系统做扩容或者缩容时,必须迁移改变了映射关系的数据,否则会出现查询不到数据的问题。
**一致性哈希算法可以解决这个问题**
使用一致性哈希算法有什么问题?
一致性哈希算法就很好地解决了分布式系统在扩容或者缩容时,发生过多的数据迁移的问题。
一致哈希算法也用了取模运算,但与哈希算法不同的是,哈希算法是对节点的数量进行取模运算,而一致哈希算法是对 2^32 进行取模运算,是一个固定的值。
我们可以把一致哈希算法是对 2^32 进行取模运算的结果值组织成一个圆环,就像钟表一样,钟表的圆可以理解成由 60 个点组成的圆,而此处我们把这个圆想象成由 2^32 个点组成的圆,这个圆环被称为哈希环,如下图:
一致性哈希要进行两步哈希:
第一步:对存储节点进行哈希计算,也就是对存储节点做哈希映射,比如根据节点的 IP 地址进行哈希;
第二步:当对数据进行存储或访问时,对数据进行哈希映射;
所以,一致性哈希是指将「存储节点」和「数据」都映射到一个首尾相连的哈希环上。
问题来了,对「数据」进行哈希映射得到一个结果要怎么找到存储该数据的节点呢?
答案是,**映射的结果值往顺时针的方向的找到第一个节点**,就是存储该数据的节点。
所以,当需要对指定 key 的值进行读写的时候,要通过下面 2 步进行寻址:
- 首先,对 key 进行哈希计算,确定此 key 在环上的位置;
- 然后,从这个位置沿着顺时针方向走,遇到的第一节点就是存储 key 的节点。
好处:当增加节点或减少节点的时候,仅影响该节点在哈希环上顺时针相邻的后继节点,其它数据也不会受到影响。
存在的问题:
负载不均:但是一致性哈希算法并不保证节点能够在哈希环上分布均匀,这样就会带来一个问题,会有大量的请求集中在一个节点上。
解决措施:: 引入虚拟节点、
- 节点数量多了后,节点在哈希环上的分布就相对均匀了
- 当节点变化时,会有不同的节点共同分担系统的变化,因此稳定性更高
引入虚拟节点后,可以会提高节点的均衡度,还会提高系统的稳定性。所以,带虚拟节点的一致性哈希方法不仅适合硬件配置不同的节点的场景,而且适合节点规模会发生变化的场景。