一致性hash算法在redis中的应用|Java 开发实战

1,395 阅读6分钟

这是我参与更文挑战的第1天,活动详情查看: 更文挑战

本文正在参加「Java主题月 - Java 开发实战」,详情查看 活动链接

前言

hello,大家好,这里是经典鸡翅。今天我们来聊聊一致性 hash 算法在 redis 上的应用。很多小伙伴的公司随着业务量的增长,数据量也在不断的加大,redis缓存也用的越来越多,这时候一台redis,往往解决不了问题,需要多台redis进行分布式存储。一旦涉及到分布式,我们就要考虑数据存储的均衡以及机器宕机带来的数据影响。那么如上,我们就抛出两个问题。第一个,如何使数据均衡的存储在每一个redis服务器上?第二个,当redis某台机器宕机的时候,如何最大限度的减少数据的丢失,减少缓存雪崩的发生。今天我们要聊的一致性hash算法就可以解决这两个问题。

原有问题的仔细分析

首先我们来聊一下传统的负载均衡的一些算法。有轮循算法、哈希算法、故障转移算法等,其中适合存储的负载均衡算法就是hash算法,也是我们比较常用的一种。hash算法的思想就是用某个设计好的hash函数,将我们要存储的数据的唯一标识,计算成一个hash值,然后使用取余的方式将hash值和机器数mod后得到余数,这样我们获得的余数一定在我们的机器中。最后进行存储即可。这个时候我们一想好像没什么问题,数据可以均衡的存储到每台机器上。那么此时如果我有一台机器挂掉了呢?机器数变小,之前的取余全部发生变化,意味着我们之前的缓存全部失效,不能从计算的机器上获取到缓存值了。意味着所有请求的数据即将瞬间打到数据库上,严重的情况下,生产环境直接挂掉。挂掉一台会有这么严重的影响,那么增加一台呢?同样的。增加一台也会导致rehash,缓存大量失效。有些小伙伴们听到这可能有点懵逼了。那我给你们举一个实际的例子。

​ 假设A公司有三台redis服务器。机器的编号我们设定为0,1,2。假设我们进来一个数据,经过hash函数计算后,值为5,那么5%3,取余为2。我们的这条数据应该存储到2号服务器上。如果2号服务器直接挂了。我们的机器数量变为2台。同样的hash值5。取余就变为了1。那么1号服务器是肯定没有他的缓存数据的,于是就要去从数据库查。少量请求还可以,如果是大量的请求全部打到数据库上,也许会造成生产服务器挂掉。

​ 那么如何进行解决呢。使用如下的一致性hash算法即可。一致性hash算法可以大大减少增加或者移除机器带来的影响。

一致性hash算法

环形hash空间

这里介绍一个重要的概念,环形Hash空间。我们设置一个从0到2^32-1的一个数字空间,并且将这个数字空间首位相连,形成一个闭环。这个hash空间用于我们将hash值映射到环上面。

image.png

数据存储到环上

接下来我们就要将我们的数据存储到环上,假设我们有四台服务器,根据服务器的ip地址进行hash到环上的位置如下。

image.png

确定了服务器的位置之后,我们用相同的hash算法,对要存储的数据的key值进行hash计算,得到的hash值映射到环上,如果没有映射到服务器,则顺时针进行查找,落到查找到的第一台服务器。

4个对象hash运算环的位置

根据此规则,我们可以看到,数据A对应到Node A,B对应到Node B,C对应到Node C,D对应到Node D。

解决原有增加或删除服务器问题

如今我们假设C节点宕机,A、B、D依然正常,那么C对象就会被重新定位到节点D。因此在一致性Hash算法中,如果一台服务器不可用,那么只会影响此服务器到前面一台服务器的数据,其它不受影响,如下所示: 在这里插入图片描述 如上面的原有问题的两种情况,另外一种就是在系统中增加一台服务器Node X,如下图所示:

在这里插入图片描述 这种情况下,A、B、D不受影响,只有对象C需要重定位到新的节点X。如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器之间数据,其它数据也不会受到影响。

Hash环的数据倾斜问题

一致性的hash算法也有不足之处,当服务节点太少时,容易因为节点分部不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器,其环分布如下: 在这里插入图片描述 按照我们顺时针查找的思想,会造成大量数据集中到节点A上,而只有极少量会定位到节点B上。那么一致性hash算法是如何解决的呢,引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器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甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。

虽然增加虚拟节点可以解决此问题,但是依然存在hash倾斜的问题,但是随着虚拟节点的大量增加,就会使得出现hash命中失败的现象降到最低。一致性hash算法公式:1-n/(n + m) * 100% 。n表示真实的redis节点,m表示增加的虚拟节点,可以看到随着虚拟节点的增加,命中失败越来越低。

总结

一致性hash算法在redis上的应用,到此就介绍完毕啦,欢迎大家评论讨论。