写点什么

深度图解 Redis Cluster 原理

发布于: 2021 年 04 月 12 日
深度图解Redis Cluster原理

不想谈好吉他的撸铁狗,不是好的程序员

前言

上文我们聊了基于 Sentinel 的 Redis 高可用架构,了解了 Redis 基于读写分离的主从架构,同时也知道当 Redis 的 master 发生故障之后,Sentinel 集群是如何执行 failover 的,以及其执行 failover 的原理是什么。


这里大概再提一下,Sentinel 集群会对 Redis 的主从架构中的 Redis 实例进行监控,一旦发现了 master 节点宕机了,就会选举出一个 Sentinel 节点来执行故障转移,从原来的 slave 节点中选举出一个,将其提升为 master 节点,然后让其他的节点去复制新选举出来的 master 节点。


你可能会觉得这样没有问题啊,甚至能够满足我们生产环境的使用需求了,那我们为什么还需要 Redis Cluster 呢?

为什么需要 Redis Cluster

的确,在数据上,有 replication 副本做保证;可用性上,master 宕机会自动的执行 failover。


那问题在哪儿呢?


首先 Redis Sentinel 说白了也是基于主从复制,在主从复制中 slave 的数据是完全来自于 master。



假设 master 节点的内存只有 4G,那 slave 节点所能存储的数据上限也只能是 4G。而且在之前的跟随杠精的视角一起来了解Redis的主从复制文章中也说过,主从复制架构中是读写分离的,我们可以通过增加 slave 节点来扩展主从的读并发能力,但是写能力存储能力是无法进行扩展的,就只能是 master 节点能够承载的上限。


所以,当你只需要存储 4G 的数据时候的,基于主从复制和基于 Sentinel 的高可用架构是完全够用的。


但是如果当你面临的是海量的数据的时候呢?16G、64G、256G 甚至 1T 呢?现在互联网的业务里面,如果你的体量足够大,我觉得是肯定会面临缓存海量缓存数据的场景的。


这就是为什么我们需要引入 Redis Cluster

Redis Cluster 是什么

知道了为什么需要 Redis Cluster 之后,我们就可以来对其一探究竟了。


那什么是 Redis Cluster 呢?


很简单,你就可以理解为 n 个主从架构组合在一起对外服务。Redis Cluster 要求至少需要 3 个 master 才能组成一个集群,同时每个 master 至少需要有一个 slave 节点。



这样一来,如果一个主从能够存储 32G 的数据,如果这个集群包含了两个主从,则整个集群就能够存储 64G 的数据。


我们知道,主从架构中,可以通过增加 slave 节点的方式来扩展读请求的并发量,那 Redis Cluster 中是如何做的呢?虽然每个 master 下都挂载了一个 slave 节点,但是在 Redis Cluster 中的读、写请求其实都是在 master 上完成的。


slave 节点只是充当了一个数据备份的角色,当 master 发生了宕机,就会将对应的 slave 节点提拔为 master,来重新对外提供服务。

节点负载均衡

知道了什么是 Redis Cluster,我们就可以继续下面的讨论了。


不知道你思考过一个问题没,这么多的 master 节点。我存储的时候,到底该选择哪个节点呢?一般这种负载均衡算法,会选择哈希算法。哈希算法是怎么做的呢?



首先就是对 key 计算出一个 hash 值,然后用哈希值对 master 数量进行取模。由此就可以将 key 负载均衡到每一个 Redis 节点上去。这就是简单的哈希算法的实现。


那 Redis Cluster 是采取的上面的哈希算法吗?答案是没有


Redis Cluster 其实采取的是类似于一致性哈希的算法来实现节点选择的。那为什么不用哈希算法来进行实例选择呢?以及为什么说是类似的呢?我们继续讨论。


因为如果此时某一台 master 发生了宕机,那么此时会导致 Redis 中所有的缓存失效。为什么是所有的?假设之前有 3 个 master,那么之前的算法应该是 hash % 3,但是如果其中一台 master 宕机了,则算法就会变成 hash % 2,会影响到之前存储的所有的 key。而这对缓存后面保护的 DB 来说,是致命的打击。

什么是一致性哈希

知道了通过传统哈希算法来实现对节点的负载均衡的弊端,我们就需要进一步了解什么是一致性哈希


我们上面提过哈希算法是对 master 实例数量来取模,而一致性哈希则是对 2^32 取模,也就是值的范围在[0, 2^32 -1]。一致性哈希将其范围抽象成了一个圆环,使用 CRC16 算法计算出来的哈希值会落到圆环上的某个地方。


然后我们的 Redis 实例也分布在圆环上,我们在圆环上按照顺时针的顺序找到第一个 Redis 实例,这样就完成了对 key 的节点分配。我们举个例子。



假设我们有 A、B、C 三个 Redis 实例按照如图所示的位置分布在圆环上,此时计算出来的 hash 值,取模之后位置落在了位置 D,那么我们按照顺时针的顺序,就能够找到我们这个 key 应该分配的 Redis 实例 B。同理如果我们计算出来位置在 E,那么对应选择的 Redis 的实例就是 A。


即使这个时候 Redis 实例 B 挂了,也不会影响到实例 A 和 C 的缓存。



例如此时节点 B 挂了,那之前计算出来在位置 D 的 key,此时会按照顺时针的顺序,找到节点 C。相当于自动的把原来节点 B 的流量给转移到了节点 C 上去。而其他原本就在节点 A 和节点 C 的数据则完全不受影响。


这就是一致性哈希,能够在我们后续需要新增节点或者删除节点的时候,不影响其他节点的正常运行。

虚拟节点机制

但是一致性哈希也存在自身的小问题,例如当我们的 Redis 节点分布如下时,就有问题了。



此时数据落在节点 A 上的概率明显是大于其他两个节点的,其次落在节点 C 上的概率最小。这样一来会导致整个集群的数据存储不平衡,AB 节点压力较大,而 C 节点资源利用不充分。为了解决这个问题,一致性哈希算法引入了虚拟节点机制



在圆环中,增加了对应节点的虚拟节点,然后完成了虚拟节点到真实节点的映射。假设现在计算得出了位置 D,那么按照顺时针的顺序,我们找到的第一个节点就是 C #1,最终数据实际还是会落在节点 C 上。


通过增加虚拟节点的方式,使 ABC 三个节点在圆环上的位置更加均匀,平均了落在每一个节点上的概率。这样一来就解决了上文提到的数据存储存在不均匀的问题了,这就是一致性哈希的虚拟节点机制。

Redis Cluster 采用的什么算法

上面提到过,Redis Cluster 采用的是类一致性哈希算法,之所以是类一致性哈希算法是因为它们实现的方式还略微有差别。


例如一致性哈希是对 2^32 取模,而 Redis Cluster 则是对 2^14(也就是 16384)取模。Redis Cluster 将自己分成了 16384 个 Slot(槽位)。通过 CRC16 算法计算出来的哈希值会跟 16384 取模,取模之后得到的值就是对应的槽位,然后每个 Redis 节点都会负责处理一部分的槽位,就像下表这样。



每个 Redis 实例会自己维护一份 slot - Redis 节点的映射关系,假设你在节点 A 上设置了某个 key,但是这个 key 通过 CRC16 计算出来的槽位是由节点 B 维护的,那么就会提示你需要去节点 B 上进行操作。


Redis Cluster 如何做到高可用

不知道你思考过一个问题没,如果 Redis Cluster 中的某个 master 节点挂了,它是如何保证集群自身的高可用的?如果这个时候我们集群需要扩容节点,它该负责哪些槽位呢?我们一个一个问题的来看一下。

集群如何进行扩容

我们开篇聊过,Redis Cluster 可以很方便的进行横向扩容,那当新的节点加入进来的时候,它是如何获取对应的 slot 的呢?


答案是通过 reshard(重新分片)来实现。reshard 可以将已经分配给某个节点的任意数量的 slot 迁移给另一个节点,在 Redis 内部是由 redis-trib 负责执行的。你可以理解为 Redis 其实已经封装好了所有的命令,而 redis-trib 则负责向获取 slot 的节点和被转移 slot 的节点发送命令来最终实现 reshard。


假设我们需要向集群中加入一个 D 节点,而此时集群内已经有 A、B、C 三个节点了。


此时 redis-trib 会向 A、B、C 三个节点发送迁移出槽位的请求,同时向 D 节点发送准备导入槽位的请求,做好准备之后 A、B、C 这三个源节点就开始执行迁移,将对应的 slot 所对应的键值对迁移至目标节点 D。最后 redis-trib 会向集群中所有主节点发送槽位的变更信息。

高可用及故障转移

Redis Cluster 中保证集群高可用的思路和实现和 Redis Sentinel 如出一辙,感兴趣的可以去看我之前写的关于 Sentinel 的文章Redis Sentinel-深入浅出原理和实战


简单来说,针对 A 节点,某一个节点认为 A 宕机了,那么此时是主观宕机。而如果集群内超过半数的节点认为 A 挂了, 那么此时 A 就会被标记为客观宕机


一旦节点 A 被标记为了客观宕机,集群就会开始执行故障转移。其余正常运行的 master 节点会进行投票选举,从 A 节点的 slave 节点中选举出一个,将其切换成新的 master 对外提供服务。当某个 slave 获得了超过半数的 master 节点投票,就成功当选。



当选成功之后,新的 master 会执行slaveof no one来让自己停止复制 A 节点,使自己成为 master。然后将 A 节点所负责处理的 slot,全部转移给自己,然后就会向集群发 PONG 消息来广播自己的最新状态。


按照一致性哈希的思想,如果某个节点挂了,那么就会沿着那个圆环,按照顺时针的顺序找到遇到的第一个 Redis 实例。


而对于 Redis Cluster,某个 key 它其实并不关心它最终要去到哪个节点,他只关心他最终落到哪个 slot 上,无论你节点怎么去迁移,最终还是只需要找到对应的 slot,然后再找到 slot 关联的节点,最终就能够找到最终的 Redis 实例了。


那这个 PONG 消息又是什么东西呢?别急,下面就会聊到。

简单了解 gossip 协议

这就是 Redis Cluster 各个节点之间交换数据、通信所采用的一种协议,叫做 gossip


gossip: 流言、八卦、小道消息
复制代码


gossip 是在 1989 年的论文上提出的,我看了一堆资料都说的是 1987 年发表的,但是文章里的时间明确是 1989 年 1 月份发表。



感兴趣的可以去看看Epidemic Algorithms for Replicated . Database Maintenance,在当时提出 gossip 主要是为了解决在分布式数据库中,各个副本节点的数据同步问题。但随着技术的发展,gossip 后续也被广泛运用于信息扩散、故障探测等等。


Redis Cluster 就是利用了 gossip 来实现自身的信息扩散的。那使用 gossip 具体是如何通信的呢?



很简单,就像图里这样。每个 Redis 节点每秒钟都会向其他的节点发送 PING,然后被 PING 的节点会回一个 PONG

gossip 协议消息类型

Redis Cluster 中,节点之间的消息类型有 5 种,分别是 MEET、PING、PONG、FAIL 和 PUBLISH。这些消息分别传递了什么内容呢?我简单总结了一下。


使用 gossip 的优劣

既然 Redis Cluster 选择了 gossip,那肯定存在一些 gossip 的优点,我们接下来简单梳理一下。


gossip 可以在 O(logN) 轮就可以将信息传播到所有的节点,为什么是 O(logN)呢?因为每次 ping,当前节点会带上自己的信息外加整个 Cluster 的 1/10 数量的节点信息,一起发送出去。你可以简单的把这个模型抽象为:


你转发了一个特别有意思的文章到朋友圈,然后你的朋友们都觉得还不错,于是就一传十、十传百这样的散播出去了,这就是朋友圈的裂变传播


当然,gossip 仍然存在一些缺点。例如消息可能最终会经过很多轮才能到达目标节点,而这可能会带来较大的延迟。同时由于节点会随机选出 5 个最久没有通信的节点,这可能会造成某一个节点同时收到 n 个重复的消息。

总结

总的来说,Redis Cluster 相当于是把 Redis 的主从架构Sentinel 集成到了一起,从 Redis Cluster 的高可用机制、判断故障转移以及执行故障转移的过程,都和主从、Sentinel 相关,这也是为什么我在之前的文章里说,主从是 Redis 高可用架构的基石。


欢迎微信搜索关注【SH 的全栈笔记】,回复【队列】获取 MQ 学习资料,包含基础概念解析和 RocketMQ 详细的源码解析,持续更新中。

好了以上就是本篇博客的全部内容了,如果你觉得这篇文章对你有帮助,还麻烦点个赞关个注分个享留个言



发布于: 2021 年 04 月 12 日阅读数: 15
用户头像

公众号【SH的全栈笔记】 2018.10.17 加入

还未添加个人简介

评论

发布
暂无评论
深度图解Redis Cluster原理