Redis第8讲——Cluster集群模式详解

news2024/11/17 21:31:54

前面两篇文章介绍了Redis主从和哨兵模式,不难发现,它们都有一些共同的缺点,首先在主从切换的过程中会丢失数据;另一个就是只有一个master,只能单点写,并没有水平扩容能力。而且每个节点都保存了所有的数据,这使得内存占用率变高,另一个就是如果进行数据恢复也比较慢。为了解决这些问题,集群模式应运而生,下面我们来一起看看。

一、什么是Redis Cluster

1.1 概念

Redis Cluster 是 Redis 数据库的一种集群方案,用于实现高可用性和横向扩展。Redis Cluster 将数据分布在多个节点上,每个节点负责存储部分数据,同时提供数据复制和自动故障转移功能,以确保系统的可靠性和性能。

Redis Cluster 通过支持分片(sharding)和复制(replication)来实现数据在集群中的分布和备份,从而在数据量增长时能够水平扩展,并且能够容忍单个节点的故障而不影响整个系统的正常运行。

在高可用上,集群基本是直接复用哨兵模式的逻辑,并且针对水平扩展进行了优化。

1.2 特点

  • 采用去中心化的集群模式,将数据按槽分布在多个Redis节点上,集群共有16384个槽,每个节点负责处理部分槽,并使用CRC16算法来计算key所属的槽:crc16(key,keylen) & 16383
  • 所有的Redis节点彼此互联,通过PING-PONG机制来进行节点间的心跳检测。
  • 客户端与Redis节点直连,不需要中间代理层(proxy)。客户端不需要连接集群中所有的节点,只用连接集群中任意一个可用节点即可。
  • 采用主从复制机制,将数据复制到多个节点上。每个主节点会有一个或多个从节点。在实际使用中,通常会将主从分布在不同机房,避免机房出现故障导致整个分片出问题,如下图。

二、在集群中执行命令

在对16384个槽指派后,集群就会进入上线状态,当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽:

  • 如果刚好是当前槽,那么节点直接执行命令。
  • 反之,节点就会返回一个MOVED错误,指引客户端转向至正确的节点,并再次发送之前想要执行的命令。

2.1 计算key属于哪个槽

ps:由于篇幅原因,这个在后续的文章中会详解。

Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis先对key使用crc16算法算出一个结果,然后用结果对16384求余数[ CRC16(key) % 16384]这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。

使用CLUSTER KEYSLOT <key>命令可以查看一个key属于哪个槽,这个命令就是通过调用上面给出的槽分配算法来实现的。

2.2 MOVED错误

当节点发现key所在的槽并非是自己负责处理的时候,节点就会向客户端返回一个MOVED错误(MOVED <slot> <ip>:<port>),其中slot为key所在槽,ip和port则是负责处理槽的节点ip和端口号。

在客户端接收到节点返回的MOVED错误时,客户端会根据MOVED错误中提供的ip和端口号,转向负责处理槽的节点,并向该节点发送之前想要执行的命令。

三、 重新分片

Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点,该过程可以在线(online)进行。

Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,步骤如下:

  • redis-trib对目标节点发送CLUSTER SETSLOT<slot>IMPORTING<source_id>命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。
  • redis-trib对源节点发送CLUSTER SETSLOT<slot>MIGRATING<target_id>命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点。
  • redis-trib向源节点发送CLUSTER GETKEYSINSLOT<slot><count>命令,获得最多count个属于槽slot的键值对的键名(key name)。
  • 对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRATE<target_ip><target_port><key_name>0<timeout>命令,将被选中的键原子地从源节点迁移至目标节点。
  • 重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。
  • redis-trib向集群中的任意一个节点发送CLUSTER SETSLOT<slot>NODE<target_id>命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经指派给了目标节点。

四、ASK错误

在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。

当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时。源节点会先在自己的数据库里面査找指定的键,如果找到的话,就直接执行客户端发送的命令。

否则,这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个 ASK 错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令,从而获取到结果。

五、MOVED和ASK的区别

从上面看,MOVED和ASK都启到了重定向客户端的效果,那么它们有什么区别呢,简单的说就是:

  • MOVED代表该槽已经完全由另一个节点负责了,会触发客户端刷新本地路由表,之后对于该槽的请求都会请求新的节点。
  • ASK只是在槽迁移中的一种临时措施,只是会产生一次重定向。

ps:本地路由表是该集群的插槽和负责处理该槽的节点地址的映射,通过该路由表,客户端可以在大部分情况下都直接请求到正确的节点,而无需重定向。

六、复制与故障转移

6.1 gossip协议

6.1.1 什么是gossip协议

这里先介绍一下gossip协议(下面会用到),全称Gossip protocol也叫Epidemic Protocol(流行病协议),那么它是干嘛的呢?

在整个Redis集群中,如果出现以下情况:

  • 新节点加入。
  • slot迁移。
  • 节点宕机。
  • slave选举成为master。

我们希望这些变化能够让整个集群的每个节点能够尽快发现,那么各个节点之间就需要相互连通并且携带相关状态数据进行传播,这就用到了gossip协议。

你可能会想到用广播的方式不也可以实现,答案是当然可以,但是对CPU和带宽的消耗比较大,所用还是采用goosip协议。

该协议的特点是,在节点数量有限的网络中,每个节点都会“随机”(也不是真正的随机,而是根据规则选择通信节点)与部分节点通信,经过一番杂乱无章(传染病)的通信后,每个节点的状态可以在一定时间内达成一致。

6.1.2 gossip协议消息

gossip协议包含多种消息,比如ping、pong、meet、fail等

  • ping:每个节点都会频繁给其它节点发送ping,包含自己的状态还有维护的集群元数据,通过ping互相交换元数据。
  • pong:返回ping和meet,包含自己的状态和其它信息,也可以用于信息广播和更新。
  • fail:某个节点判断另一个节点fail后,就发送fail通知其它节点该节点下线了。
  • meet:当发送者接到客户端发送CLUSTER MEET命令时,发送者会向接收者发送MEET消息,请求接收者加入到发送者所在的集群里面。

6.2 复制

集群的每个分片都使用主从复制保证高可用,之前的文章中已经介绍过了,就不再赘述了。

6.3 故障检测

与哨兵模式的故障检测核心思想基本一致。

集群中的每个节点都会定期地向集群中的其它节点发送PING消息,以此来检测是否在线,发送PING消息的时机主要有两个:

  • 每秒执行一次:随机检查5个节点,选出最早收到PONG回复的节点,即最久没有通信过的节点,发送PING消息。
  • 每100毫秒执行一次:轮询集群的节点,对于正常节点,如果上次收到该节点的PONG回复的时间距离现在,已经超过集群超时时间的一半(cluster-node-timeout/2)则直接向该节点发送PING消息。

如果接收 PING 消息的节点在规定的时间内(cluster-node-timeout,默认15秒-conf配置文件),没有返回 PONG 回复或者发送其他任何消息,那么发送 PING 消息的节点就会将接收 PING 消息的节点标记为疑似下线probable failure,PFAIL)。

ps:这边 Redis 没有将 PONG 回复作为目标节点存活的唯一证明,而是将目标节点的任何消息都作为存活的证明。这是因为在集群负载较高的时候,收到 PONG 回复可能会出现延迟。

如果一个集群里面,半数以上主节点都将某个主节点x报告为疑似下线,那么该主节点x将被标记为已下线FAIL),将主节点x标记为已下线的节点会向集群广播一条关于主节点x的FAIL消息,所有收到消息的节点会立即将主节点x标记为已下线。

6.4 选举新的主节点

故障转移的第一步就是选举新的主节点,该方法和选举领头Sentinel的方法非常相似,因为两者都是基于Raft算法实现的。

6.4.1 epoch(纪元)

在介绍该流程之前我们先了解一个名词epoch(纪元):Redis集群中使用了类似Raft算法term(任期)的概念成为epoch(纪元),在Redis集群中主要有两种:

  • currentEpoch(集群配置纪元):是一个集群范围内的全局递增计数器,用于跟踪集群的状态变化。每当集群发生状态变化,如节点加入、离开、主从切换等,currentEpoch就会增加。这有助于确保集群中的各个节点都了解到最新的集群状态。例如,当一个新的节点加入集群时,它会从其他节点获取当前的 currentEpoch,以确保它了解到最新的集群状态。
  • configEpoch(节点配置纪元):是每个节点上的参数,表示节点在处理集群配置时的版本号。当一个节点成为集群中的主节点时,它的 configEpoch 会增加,并且这个增加的值会被广播给整个集群,以通知其他节点。这个值在 Redis 集群中被用来判断一个节点是否已经接受了来自其他节点的新的配置。如果一个节点的 configEpoch 落后于其他节点,则说明它需要更新配置以保持与集群的一致性。这对于确保集群的配置是一致的非常重要,特别是在故障恢复、主从切换等情况下。

6.4.2 流程

  • 当从节点发现自己正在复制的主节点进入已下线状态时,会发起一次选举:将 currentEpoch 加1,然后向集群广播一条 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 消息,要求所有收到这条消息、并且具有投票权的主节点向这个从节点投票。

  • 其它节点收到消息后,会判断是否要给发消息的节点投票:

    • 当前节点是slave或master,但不负责处理槽,则当前节点没有投票权,直接返回。

    • 请求节点的currentEpoch小于当前节点,校验失败。因为发送者的状态与当前集群不一致,可能是长时间下线的节点刚刚上线,直接返回。

    • 当前节点已经投过票了,检验失败。

    • 请求节点是master或为空,检验失败。

    • 请求节点的master没有故障,并且不是手动故障转移,检验失败。

    • 上一次为该master投票时间在cluster-node-timeout的2倍范围之内,检验失败。这个用于使获胜从节点有时间将其成为新主节点的消息通知给其他从节点,从而避免另一个从节点发起新一轮选举又进行一次没必要的故障转移。

    • 请求节点的configEpoch如果比之前负责这些槽位的节点小,检验失败。

  • 通过以上校验,主节点才会向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,从节点会根据收到这个消息的条数来统计获得了多少个主节点的支持(票数)。

  • 当一个从节点收集到N/2+1张票支持(N代表具有投票权的主节点)时,这个从节点就会当选为新的主节点。(每个主节点只能投一次票)

  • 如果在一个配置纪元内所有从节点都没能收到足够的票,那么集群会开启新的纪元,再次选举,直到选出为止。

6.5 故障转移

当slave发现自己正在复制的master处于已下线(FAIL)状态时,slave会对下线的matser进行故障转移:

  • 发起一次选举,该下线的master的所有slave里面,会有一个slave被选中。
  • 被选中的slave会执行SLAVEOF no one命令,成为新的master。
  • 新的master会撤销所有对已下线master的槽指派,并将这些槽全部指向自己。
  • 新的master向集群广播一条PONG消息,这条PONG消息可以让集群中的其它节点立即知道这个节点已经“上位”了,并且已经接管了原本由已下线节点负责处理的槽。集群中的其他节点收到消息后会更新自己保存的相关配置信息。
  • 新的master开始接收和自己负责处理的槽相关的命令请求,至此,故障转移完成。

七、优缺点

优点:

  • Redis Cluster 采用分布式架构,自动将数据分片存储在多个节点上,提供了良好的横向扩展性和高可用性。
  • 集群节点可以自动发现和重新分片,并且支持节点动态扩容和缩减,减少了集群管理的复杂性。
  • 当新的节点加入或离开集群时,Redis Cluster 能够自动进行节点发现和重新分片,而无需手动干预,这简化了集群管理的复杂度。

缺点:

  • Redis Cluster 不支持跨分片的事务操作,事务必须在同一分片上执行,这可能会在某些场景下带来限制。
  • 配置和管理 Redis Cluster 相对复杂,需要理解分布式系统的原理和网络分区等问题。需要投入更多的精力来实施和维护集群。

End:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1515204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

请说一下卷积神经网络里的特征图和感受野怎么计算?VGG网络的特点?如何解释?

请说一下卷积神经网络里的特征图和感受野怎么计算&#xff1f; 特征图的计算 首先要明确什么是特征图&#xff1f; 特征图是卷积层输出的二维数组&#xff0c;每个元素表示一个特定区域的特征。特征图的大小取决于输入图像的大小、卷积核的大小、步幅&#xff08;stride&…

专升本 C语言笔记-08 goto语句

goto语句 无条件跳转运算符(凡是执行到goto语句会直接跳转到 定义的标签) 缺点&#xff1a;滥用goto语句将会导致逻辑混乱&#xff0c;导致系统崩溃等问题! ! ! 代码演示 int i 0; //定义标签 jump(名字随便起哦) jump:printf("%d ",i); i; if(i < 10)goto j…

JS高级_数据类型

undefined与null的区别? undefined代表没有赋值null代表赋值了, 只是值为null // 1. undefined与null的区别?var a1var a2 nullconsole.log(a1, a2)什么时候给变量赋值为null呢? var a null //已经确定a是一个对象, 但还没具体赋值&#xff08;开始&#xff09;a null …

软考高级:系统工程方法(霍尔三维结构、切克兰德方法等)概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

六 超级数据查看器 讲解稿 详情1 概述

六 超级数据查看器 讲解稿 详情1 概述 点此此处 以新界面 打开B站 当前视频教程 APP下载地址 百度 下载地址 ​ 讲解稿全文&#xff1a; 大家好&#xff0c;今天我们讲解一下超级数据查看器详情界面。由于内容较多&#xff0c;讲解要分为7集&#xff0c;这是第一集 首…

云仓酒庄北京朝阳区旗舰店发布活动盛况:红酒品鉴沙龙共筑美好

原标题&#xff1a;云仓酒庄北京朝阳区旗舰店活动盛况&#xff1a;红酒品鉴沙龙与招商交流共筑美好未来 在繁忙的都市中&#xff0c;有一片静谧的天地&#xff0c;那便是云仓酒庄北京朝阳区旗舰店。这里不仅是红酒爱好者的聚集地&#xff0c;更是商业交流的新平台。近日&#…

算法详解——图的深度优先遍历和广度优先遍历

目录 一、图结构二、深度优先遍历2.1 图的遍历2.2 深度优先遍历过程2.3 深度优先遍历核心思想2.4 深度优先遍历实现 三、广度优先遍历3.1 广度优先遍历过程3.2 广度优先遍历核心思想3.3 广度优先遍历实现 参考文献 一、图结构 图结构指的是如下图所示的由节点和边组成的数据。 …

AUTOSAR软件配置(3):MCAL下载安装

前言 所有的NXP软件的下载安装都是需要自己在官网去注册账号的 中文的NXP官方网址&#xff1a;恩智浦半导体官方网站 | NXP 半导体 注&#xff1a;本文指导安装教程将以S32K144平台为例展开。 下载 找到下载入口的指引 然后在左侧的导航栏找到AUTOSAR 然后选择4.2版本 在…

【MySQL探索之旅】MySQL数据表的增删查改(初阶)

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &…

kubernetes之概念入门篇

K8S的内容是要比docker多很多的。 kubernetes中文官网&#xff1a; Kubernetes(K8S)中文文档_Kubernetes中文社区 1、认识kubernetes 1.1、什么是kubernetes&#xff1f; kubernetes是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;kubernetes…

鸿蒙API9+axios封装一个通用工具类

使用方式&#xff1a; 打开Harmony第三方工具仓&#xff0c;找到axios&#xff0c;如图&#xff1a; 第三方工具仓网址&#xff1a;https://ohpm.openharmony.cn/#/cn/home 在你的项目执行命令&#xff1a;ohpm install ohos/axios 前提是你已经装好了ohpm &#xff0c;如果没…

Hive和MySQL的部署、配置Hive元数据存储到MySQL、Hive服务的部署

目录 前言 一、Hive安装配置 二、MySQL安装配置 三、Hive元数据存储到MySQL 四、Hive服务的部署 前言 Hive 定义&#xff1a; Hive 是基于 Hadoop 的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张数据库表&#xff0c;并提供简单的 SQL 查询功能&#…

音视频开发之旅(75)- AI数字人进阶--GeneFace++

目录 1.效果展示和玩法场景 2.GeneFace原理学习 3.数据集准备以及训练的过程 5.遇到的问题与解决方案 6.参考资料 一、效果展示 AI数字人进阶--GeneFace&#xff08;1&#xff09; AI数字人进阶--GeneFace&#xff08;2&#xff09; 想象一下&#xff0c;一个专为你打造的…

MyBatis3源码深度解析(十一)MyBatis常用工具类(四)ObjectFactoryProxyFactory

文章目录 前言3.6 ObjectFactory3.7 ProxyFactory3.8 小结 前言 本节研究ObjectFactory和ProxyFactory的基本用法&#xff0c;因为它们在MyBatis的源码中比较常见。这里不深究ObjectFactory和ProxyFactory的源码&#xff0c;而是放到后续章节再展开。 3.6 ObjectFactory Obj…

最优算法100例之05-第一个只出现一次的字符

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置。 题解报…

掌握抽象基础之20个必备原则,看完你还不会,你打我

抽象基础之20个必备原则 1. 面向对象编程&#xff08;OOP&#xff09;中抽象原则背后的基本思想是什么&#xff1f;2.抽象和封装的区别&#xff1f;3.提供一个现实生活中说明抽象的例子4.在编程语言中如何实现抽象&#xff1f;5.定义抽象类6.提供一个抽象类的真实世界场景7.解释…

Composer创建ThinkPHP无法获取最新版本的问题

composer安装TP&#xff08;截止目前最新版本为8.0&#xff09; composer create-project topthink/think tp 一开始直接给我安装成TP6了&#xff0c;原因就是我系统的PHP版本不是8.0以上&#xff0c;所以不支持最新的TP版本&#xff0c;就会默认安装之前稳定的版本。解决这个…

25.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-利用全新的通信结构传递消息

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;24.根据配置文件…

备考2025年AMC8竞赛:吃透2000-2024年600道真题(免费赠送真题)

我们继续来随机看五道AMC8的真题和解析&#xff0c;根据实践经验&#xff0c;对于想了解或者加AMC8美国数学竞赛的孩子来说&#xff0c;吃透AMC8历年真题是备考最科学、最有效的方法之一。 即使不参加AMC8竞赛&#xff0c;吃透了历年真题600道和背后的知识体系&#xff0c;那么…

18. 查看帖子详情

文章目录 一、建立路由二、开发GetPostDetailHandler三、编写logic四、编写dao层五、编译测试运行 一、建立路由 router/route.go v1.GET("/post/:id", controller.GetPostDetailHandler)二、开发GetPostDetailHandler controller/post.go func GetPostDetailHand…