Redis系列5:深入分析Cluster 集群模式

news/2024/5/19 0:35:21

1 背景

前面我们学习了Redis高可用的两种架构模式:主从模式、哨兵模式。
解决了我们在Redis实例发生故障时,具备主从自动切换、故障转移的能力,终保证服务的高可用。
但是这些其实远远不够,随着我们业务规模的不断扩展,用户量膨胀,并发量持续提升。原有的主从架构,已经远远达不到我们的需求了,这时候会有一些问题出现,比如:

  • 单机的CPU、内存、连接数、计算力都是有极限的,不能无限制的承载流量的扩增。
  • 超额的请求、大规模的数据计算,导致必然的慢响应。
    这时候就需要适当的推进架构的演进,来满足发展的需要。

2 Cluster 模式介绍

2.1 什么是Cluster模式

Cluster 即 集群模式,类似MySQL,Redis 集群也是一种分布式数据库方案,集群通过分片(sharding)模式来对数据进行管理,并具备分片间数据复制、故障转移和流量调度的能力。这种 分治模式很常见,我们在 微服务系列:拆分策略 和 MySQL系列:分库分表 中实践过很多次了。
Redis集群的做法是 将数据划分为 16384(2的14次方)个哈希槽(slots),如果你有多个实例节点,那么每个实例节点将管理其中一部分的槽位,槽位的信息会存储在各自所归属的节点中。以下图为例,该集群有4个 Redis 节点,每个节点负责集群中的一部分数据,数据量可以不均匀。比如性能好的实例节点可以多分担一些压力。

一个Redis集群一共有16384个哈希槽,你可以有1 ~ n个节点来分配这些哈希槽,可以不均匀分配,每个节点可以处理0个 到至多 16384 个槽点。
当16384个哈希槽都有节点进行管理的时候,集群处于online 状态。同样的,如果有一个哈希槽没有被管理到,那么集群处于offline状态。

上面图中4个实例节点组成了一个集群,集群之间的信息通过 Gossip协议 进行交互,这样就可以在某一节点记录其他节点的哈希槽(slots)的分配情况。

2.2 为什么需要Cluster模式

单机的吞吐无法承受持续扩增的流量的时候,好的办法是从横向(scale out) 和 纵向(scale up)两方面进行扩展,这个我们在 MySQL系列 和 微服务系列 的时候已经讨论过了。

  • 纵向扩展(scale up):将单个实例的硬件资源做提升,比如 CPU核数量、内存容量、SSD容量。
  • 横向扩展(scale out):横向扩增 Redis 实例数,这样每个节点只负责一部分数据就可以,分担一下压力,典型的分治思维。
                 那横向扩展和纵向扩展各有什么优缺点呢?
  • scale up 虽然操作起来比较简易。但是没法解决Redis一些瓶颈问题,比如持久化(如轮式RDB快照还是AOF指令),遇到大数据量的时候,照样效率会很低,响应慢。另外,单台服务机硬件扩容也是有限制的,不可能无限操作。
  • scale out 更容易扩展,分片的模式可以解决很多问题,包括单一实例节点的硬件扩容限制、成本限制,还可以分摊压力,精细化治理,精细化维护。但是同时也要面临分布式带来的一些问题
    现实情况下,在面对千万级甚至亿级别的流量的时候,很多大厂都是在千百台的实例节点组成的集群上进行流量调度、服务治理的。所以,使用Cluster模式,是业内广泛采用的模式。

3 Cluster 实现原理

3.1 集群的组群过程

集群是由一个个互相独立的节点(readis node)组成的, 所以刚开始的时候,他们都是隔离,毫无联系的。我们需要通过一些操作,把他们聚集在一起,终才能组成真正的可协调工作的集群。
各个节点的联通是通过 CLUSTER MEET 命令完成的:CLUSTER MEET <ip> <port> 。
具体的做法是其中一个node向另外一个 node(指定 ip 和 port) 发送 CLUSTER MEET 命令,这样就可以让两个节点进行握手(handshake操作) ,握手成功之后,node 节点就会将握手另一侧的节点添加到当前节点所在的集群中。
这样一步步的将需要聚集的节点都圈入同一个集群中,如下图:

3.2 集群数据分片原理

现在的Redis集群分片的做法,主要是使用了官方提供的 Redis Cluster 方案。这种方案就是的核心就是集群的实例节点与哈希槽(slots)之间的划分、映射与管理。下面我们来看看他具体的步骤。

3.2.1 哈希槽(slots)的划分

这个前面已经说过了,我们会将整个Redis数据库划分为16384个哈希槽,你的Redis集群可能有n个实例节点,每个节点可以处理0个 到至多 16384 个槽点,这些节点把 16384个槽位瓜分完成。
而你实际存储的Redis键值信息也必然归属于这 16384 个槽的其中一个。slots 与 Redis Key 的映射是通过以下两个步骤完成的:

  • 使用 CRC16 算法计算键值对信息的Key,会得出一个 16 bit 的值。
  • 将 第1步中得到的 16 bit 的值对 16384 取模,得到的值会在 0 ~ 16383 之间,映射到对应到哈希槽中。
    当然,可能在一些特殊的情况下,你想把某些key固定到某个slot上面,也就是同一个实例节点上。这时候可以用hash tag能力,强制 key 所归属的槽位等于 tag 所在的槽位。
    其实现方式为在key中加个{},例如test_key{1}。使用hash tag后客户端在计算key的crc16时,只计算{}中数据。如果没使用hash tag,客户端会对整个key进行crc16计算。下面演示下hash tag使用:
127.0.0.1:6380> cluster keyslot user:case{1}
(integer) 1024
127.0.0.1:6380> cluster keyslot user:favor
(integer) 1023
127.0.0.1:6380> cluster keyslot user:info{1}
(integer) 1024

如上,使用hash tag 后会对应到通一个hash slot:1024中。

3.2.2 哈希槽(slots)的映射

一种是初始化的时候均匀分配 ,使用 cluster create 创建,会将 16384 个slots 平均分配在我们的集群实例上,比如你有n个节点,那每个节点的槽位就是 16384 / n 个了 。
另一种是通过 CLUSTER MEET 命令将 node1、node2、ndoe3、node4 4个节点联通成一个集群,刚联通的时候因为还没分配哈希槽,还是处于offline状态。我们使用 cluster addslots 命令来指定。
指定的好处就是性能好的实例节点可以多分担一些压力。

可以通过 addslots 命令指定哈希槽范围,比如下图中,我们哈希槽是这么分配的:实例 1 管理 0 ~ 7120 哈希槽,实例 2 管理 7121~9945 哈希槽,实例 3 管理 9946 ~ 13005 哈希槽,实例 4 管理 13006 ~ 16383 哈希槽。

redis-cli -h 192.168.0.1 –p 6379 cluster addslots ,7120
redis-cli -h 192.168.0.2 –p 6379 cluster addslots 7121,9945
redis-cli -h 192.168.0.3 –p 6379 cluster addslots 9946,13005
redis-cli -h 192.168.0.4 –p 6379 cluster addslots 13006,16383

slots 和 Redis 实例之间的映射关系如下:

key testkey_1 和 testkey_2 经过 CRC16 计算后再对slots的总个数 16384 取模,结果分别匹配到了 cache1 和 cache3 上。

3.3 数据复制过程和故障转移

3.3.1 数据复制

Cluster 是具备Master 和 Slave模式,Redis 集群中的每个实例节点都负责一些槽位,比如上图中的四个节点分管了不同的槽位区间。而每个Master至少需要一个Slave节点,Slave 节点是通过《Redis系列3:高可用之主从架构》方式同步主节点数据。 节点之间保持TCP通信,当Master发生了宕机, Redis Cluster自动会将对应的Slave节点选为Master,来继续提供服务。与纯主从模式不同的是,主从节点之间并没有读写分离, Slave 只用作 Master 宕机的高可用备份,所以更合理来说应该是主备模式。
如果主节点没有从节点,那么一旦发生故障时,集群将完全处于不可用状态。 但也允许配置 cluster-require-full-coverage参数,及时部分节点不可用,其他节点正常提供服务,这是为了避免全盘宕机。
主从切换之后,故障恢复的主节点,会转化成新主节点的从节点。这种自愈模式对提高可用性非常有帮助。

3.3.2 故障检测

一个节点认为某个节点宕机不能说明这个节点真的挂起了,无法提供服务了。只有占据多数的实例节点都认为某个节点挂起了,这时候cluster才进行下线和主从切换的工作。
Redis 集群的节点采用 Gossip 协议来广播信息,每个节点都会定期向其他节点发送ping命令,如果接受ping消息的节点在指定时间内没有回复pong,则会认为该节点失联了(PFail),则发送ping的节点就把接受ping的节点标记为主观下线。
如果集群半数以上的主节点都将主节点 xxx 标记为主观下线,则节点 xxx 将被标记为客观下线,然后向整个集群广播,让其它节点也知道该节点已经下线,并立即对下线的节点进行主从切换。

3.3.3 主从故障转移

当一个从节点发现自己正在复制的主节点进入了已下线,则开始对下线主节点进行故障转移,故障转移的步骤如下:

  • 如果只有一个slave节点,则从节点会执行SLAVEOF no one命令,成为新的主节点。

  • 如果是多个slave节点,则采用选举模式进行,竞选出新的Master

    • 集群中设立一个自增计数器,初始值为 0 ,每次执行故障转移选举,计数就会+1。
    • 检测到主节点下线的从节点向集群所有master广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,所有收到消息、并具备投票权的主节点都向这个从节点投票。
    • 如果收到消息、并具备投票权的主节点未投票给其他从节点(只能投一票哦,所以投过了不行),则返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示支持。
    • 参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,如果收集到的选票 大于等于 (n/2) + 1 支持,n代表所有具备选举权的master,那么这个从节点就被选举为新主节点。
    • 如果这一轮从节点都没能争取到足够多的票数,则发起再一轮选举(自增计数器+1),直至选出新的master。
  • 新的主节点会撤销所有对已下线主节点的slots指派,并将这些slots全部指派给自己。

  • 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。

  • 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。

跟哨兵类似,两者都是基于 Raft 算法来实现的,流程如图所示:

3.4 client 访问 数据集群的过程

3.4.1 定位数据所在节点

我们前面说过了,Redis 中的每个实例节点会将自己负责的哈希槽信息 通过 Gossip 协议广播给集群中其他的实例,实现了slots分配信息的扩散。这样的话,每个实例都知道整个集群的哈希槽分配情况以及映射信息。
所以客户端想要快捷的连接到服务端,并对某个redis数据进行快捷访问,一般是经过以下步骤:

  • 客户端连接任一实例,获取到slots与实例节点的映射关系,并将该映射关系的信息缓存在本地。
  • 将需要访问的redis信息的key,经过CRC16计算后,再对16384 取模得到对应的 Slot 索引。
  • 通过slot的位置进一步定位到具体所在的实例,再将请求发送到对应的实例上。
    下图展示了 Redis 客户端如何定位数据所在节点:

4 总结

  • 哨兵模式已经实现了故障自动转移的能力,但业务规模的不断扩展,用户量膨胀,并发量持续提升,会出现了 Redis 响应慢的情况。
  • 使用 Redis Cluster 集群,主要解决了大数据量存储导致的各种慢问题,同时也便于横向拓展。在面对千万级甚至亿级别的流量的时候,很多大厂的做法是在千百台的实例节点组成的集群上进行流量调度、服务治理的。
  • 整个Redis数据库划分为16384个哈希槽,Redis集群可能有n个实例节点,每个节点可以处理0个 到至多 16384 个槽点,这些节点把 16384个槽位瓜分完成。
  • Cluster 是具备Master 和 Slave模式,Redis 集群中的每个实例节点都负责一些槽位,节点之间保持TCP通信,当Master发生了宕机, Redis Cluster自动会将对应的Slave节点选为Master,来继续提供服务。
  • 客户端能够快捷的连接到服务端,主要是将slots与实例节点的映射关系存储在本地,当需要访问的时候,对key进行CRC16计算后,再对16384 取模得到对应的 Slot 索引,再定位到相应的实例上。实现高效的连接。

http://www.mrgr.cn/p/16046776

相关文章

Anaconda中安装pyecharts

学习Python的过程中发现conda无法使用pyecharts https://pypi.org/project/pyecharts/#files 下载后将whl文件复制到到本地conda\scripts目录下 随后打开conda 切换到script目录下,输入命令 pip install pyecharts-2.0.5-py3-none-any.whl 等待安装完成 在pycharm中配置Python…

【ZYNQ】zynq启动模式及程序固化

一、前言 由于zynq含有arm cpu ,其启动模式由ps主导&#xff0c;与纯逻辑的fpga不相同&#xff0c;此处做一个记录。 二、zynq启动模式 关于zynq的启动模式详细内容可以参考官方文档&#xff1a;ug585-Zynq 7000 SoC Technical Reference Manual&#xff0c;第六章。 2.1 启…

网络拓扑—FTP服务搭建

均使用Windows Server 2003进行搭建目录FTP服务搭建网络拓扑配置网络FTPPC安装FTP服务配置FTP服务FTP用户配置—1PC机访问FTP站点IE浏览器访问终端访问FTP用户配置—2PC机访问ftp站点IE浏览器访问终端访问 FTP服务搭建 网络拓扑//交换机忽略不计 FTP服务IP:192.168.1.1 PC机IP…

【研发管理】产品经理知识体系-产品创新中的市场调研

导读&#xff1a;在产品创新过程中&#xff0c;市场调研的重要性不言而喻。它不仅是产品创新的起点&#xff0c;也是确保产品成功推向市场的关键步骤。对于产品经理系统学习和掌握产品创新中的市场调研相关知识体系十分重要。 目录 概述&#xff1a;市场调研重要性 1、相关概…

10:00面试,10:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

【Java--数据结构】链表经典OJ题详解(上)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 谈谈头插、头删、尾插、头插的时间复杂度 反转一个单链表 链表的中间结点 返回倒数第k个结点 合并两个链表 谈谈头插、头删、尾插、头插的时间复杂度 头插和头删的时…

顺序栈--代码题

数据结构 顺序栈代码题设计一个进制转换程序,使用顺序栈设计一个把十进制转化为十六进制的接口,实现当键盘输入一个非负的十进制时,可以在终端输出对应的十六进制数。 /***************************************************************************************** file n…

完美运营版商城/拼团/团购/秒杀/积分/砍价/实物商品/虚拟商品等全功能商城

源码下载地址&#xff1a;完美运营版商城.zip 后台可以自由拖曳修改前端UI页面 还支持虚拟商品自动发货等功能 挺不错的一套源码 前端UNIAPP 后端PHP 一键部署版本

aws安装jenkins步骤

一、aws安装jdk11 1.1 aws安装jdk11 1、切换root,更新yum, sudo su yum update exist 2、安装JDK1.8版本 yum -y list java-1.8.0* #(安装jdk11,yum -y list java-11*) yum install java-1.8.0-openjdk-devel.x86_64 #(安装jdk11,yum -y list java-11-openjdk-deve…

嵌入式4-24

作业&#xff1a; 整理思维导图 定义一个矩形类Rec&#xff0c;包含私有属性length&#xff0c;width&#xff0c;有以下成员函数&#xff1a; void set_length(int l); //设置长度 void set_width(int w); //设置宽度 int get_length(); //获取长度 int get_width(); //获取宽…

【GIS教程】ArcGIS做日照分析(附练习数据下载)

我国对住宅日照标准的规定是:冬至日住宅底层日照不少于1小时或大寒日住宅层日照不少于2小时(通常以当地冬至日正午12时的太阳高度角作为依据)。因冬至日太阳高度角最低&#xff0c;照射范围最小&#xff0c;如果冬至日12&#xff1a;00建筑物底层能够接收到阳光&#xff0c;那么…

Golang | Leetcode Golang题解之第42题接雨水

题目&#xff1a; 题解: func trap(height []int) (ans int) {n : len(height)if n 0 {return}leftMax : make([]int, n)leftMax[0] height[0]for i : 1; i < n; i {leftMax[i] max(leftMax[i-1], height[i])}rightMax : make([]int, n)rightMax[n-1] height[n-1]for i…

详解MySQL C API 相关接口(大白话就是:MySQL的c语言怎么写)

文章目录 1、C API 官方文档2、初始化 MYSQL3、连接 MySQL设置连接字符集&#xff08;使得客户端编码方式匹配&#xff09; 4、下发 mysql 指令5、获取 mysql 查询结果(保存起来)获取行与列遍历存储结果 6、释放 MYSQL\_RES 对象7、关闭 MySQL 连接8、总结 1、C API 官方文档 …

vue3 删除对象中的属性,可以使用js里的delete,但需注意ts定义对象类型!

如上如&#xff0c;当使用delete 删除stateData中的属性时&#xff0c; 报错&#xff0c;意思为 TypeScript 错误“‘delete’ 运算符的操作数必须是可选的 什么原因呢&#xff1f;是因为我偷懒 缺少了ts定义类型 方法一&#xff1a; &#xff08;不推荐&#xff09; delete …

嵌入式笔记4.1 GPIO 功能复用

目录一、了解 MCU(GPIO)具有的所有复用功能通过查看 MCU 的数据手册可以知道 MCU 的所有引脚的功能:例 STM32L431:例 stm32f103:复用、重映射、多路复用(多功能引脚)GPIO复用(AF - Alternate Function)重映射(Remapping)多路复用(Multi-function)常见引脚功能一览…

【八股】Redisson分布式锁

Redisson分布式锁 主要了解了Redisson分布式锁实现的三个功能&#xff1a; 1.可重入 -> 防止死锁 2.可重试&#xff08;i.e. 非阻塞获取锁&#xff09; 3.自动续约 1. 可重入 原理&#xff1a; 利用Redis的Hash结构&#xff0c;记录了使用当前锁的线程id和重用次数&#…

如何分析和优化慢sql语句

前言 sql查询速度比较慢容易成为性能瓶颈,这时我们可以优化我们的sql语句或数据库表 一般sql语句执行很慢的种类分为: 1.聚合查询 2.多表查询 3.表数据量过大查询 4.深度分页查询 这四种的前三种都可以通过优化sql语句来优化sql查询速度 正文 聚合查询 我们可以通过尝…

初始C++

1. C关键字(C98) C总计63个关键字&#xff0c; C语言32个关键字 ps&#xff1a;下面我们只是看一下C有多少关键字&#xff0c;不对关键字进行具体的讲解。后面我们学到以后再 细讲。 2. 命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;…

ps/lr如何为一个型号相机的raw使用其他相机的预设

首先单独下载camera raw,进到C:\ProgramData\Adobe\CameraRaw\CameraProfiles\Camera中获取想要的相机型号的预设dcp文件 去 https://liquidtelecom.dl.sourceforge.net/project/dcptool/dcptool/dcpTool V1.11.0/dcpTool_1_11_0.zip?viasf=1 下载dcp编译工具dcpTool cd C:\U…

Oracle 21 C 安装详细操作手册,并配置客户端连接

Oracle 21 C 安装详细操作手册 Win 11 Oracle 21C 下载&#xff1a; Database Software Downloads | Oracle 中国 云盘共享 链接&#xff1a;https://pan.baidu.com/s/12XCilnFYyLFnSVoU_ShaSA 提取码&#xff1a;nfwc Oracle 21C 配置与登陆&#xff1a; 开始菜单 NetMa…