0x02.Redis 集群的实现原理是什么?

news2025/4/15 1:50:26

回答重点

Redis 集群(Redis cluster)是通过多个 Redis 实例组成的,每个主节点实例负责存储部分的数据,并且可以有一个或多个从节点作为备份

具体是采用哈希槽(Hash Slot)机制来分配数据,将整个键空间划分为 16384 个槽(slots)。每个 Redis 主节点实例负责一定范围的哈希槽,数据的 key 经过 CRC16-CCITT 哈希算法计算后对 16384 取余即可定位到对应的节点。

客户端在发送请求时,会通过集群的任意节点进行连接,如果该节点存储了对应的数据则直接返回,反之该节点会根据请求的键值计算哈希槽并路由到正确的节点。现代 Redis 客户端通常会缓存槽位映射信息,避免频繁重定向。

简单来说,集群就是通过多台机器分担单台机器上的压力,同时通过主从复制提供高可用性。

扩展知识

Redis 集群中节点之间的信息如何同步?

Redis 集群内每个节点都会保存集群的完整拓扑信息,包括每个节点的 ID、IP 地址、端口、负责的哈希槽范围等。

节点之间使用 Gossip 协议进行状态交换,以保持集群的一致性和故障检测。每个节点会周期性地发送 PING 和 PONG 消息,交换集群信息,使得集群信息得以同步。

Gossip 的优点

  • 最终一致性:Gossip 协议能够在一段时间后使集群信息达成一致,虽然传播速度不如中心化方案快,但网络开销更小。
  • 降低网络负担:由于信息是以随机节点间的对话方式传播,避免了集中式的状态查询,从而降低了网络流量。

Gossip 协议

Gossip 主要特点

  1. 分布式信息传播:每个节点定期向其他节点发送其状态信息,确保所有节点对集群的状态有一致的视图。
  2. 低延迟和高效率:Gossip 协议设计为轻量级的通信方式,能够快速传播信息,减少单点故障带来的风险。
  3. 去中心化:没有中心节点,所有节点平等地参与信息传播,提高了系统的鲁棒性。

工作原理

  1. 状态报告:每个节点在特定的时间间隔内,向随机选择的其他节点发送其自身的状态信息,包括节点的主从关系、槽位分布等。
  2. 信息更新:接收到状态信息的节点会根据所接收到的数据更新自己的状态,并将更新后的状态继续传播给其他节点。
  3. 节点检测:通过周期性交换状态信息,节点可以检测到其他节点的存活状态。如果某个节点未能在预定时间内响应,则该节点会被标记为故障节点。
  4. 容错处理:在检测到节点故障后,集群中的其他节点可以采取措施(如重新分配槽位)以保持系统的高可用性。

Redis 集群分片原理图示

Redis 集群会将数据分散到 16384(2^14)个哈希槽中,集群中的每个主节点负责一定范围的哈希槽,在 Redis 集群中,使用 CRC16-CCITT 哈希算法计算键的哈希槽,以确定该键应存储在哪个节点。

集群哈希槽分片如下图所示:

在这里插入图片描述

每个节点会拥有一部分的槽位,然后对应的键值会根据其本身的 key,映射到一个哈希槽中,其主要流程如下:

  • 根据键值的 key,按照 CRC16-CCITT 算法计算一个 16 bit 的值,然后将 16 bit 的值对 16384 进行取余运算,最后得到一个对应的哈希槽编号。
  • 值得注意的是,Redis 支持哈希标签(Hash Tags)机制,即如果键名中包含 “{…}” 格式的内容,那么只有花括号内的内容会被用来计算哈希槽,这样可以确保相关联的键被分配到同一个槽中。
  • 根据每个节点分配的哈希槽区间,对应编号的数据落在对应的区间上,就能找到对应的分片实例。

为了方便大家理解,我这里画一个对应的关系图,以三个节点为例:

Redis 哈希槽映射图


CRC16(Key1) % 16384 = 123
CRC16(Key2) % 16384 = 8275
CRC16(Key3) % 16384 = 12319
Redis实例 1(0 ~ 5460)
Redis实例 2(5461 ~ 10922)
Redis实例 3(10923 ~ 16383)

这里还有一点需要强调下,Redis 客户端可以访问集群中任意一台实例,正常情况下这个实例包含这个数据
但如果槽被转移了,客户端还未来得及更新槽的信息,当前实例没有这个数据,则返回 MOVED 响应给客户端,将其重定向到对应的实例(因 Gossip 协议确保集群内每个节点都会保存集群的完整拓扑信息)。

Redis 集群中存储 key 示例

假设我们有一个 Redis 集群,包含三个主节点(Node1、Node2、Node3),它们分别负责以下哈希槽:

  • Node1: 哈希槽 0-5460
  • Node2: 哈希槽 5461-10922
  • Node3: 哈希槽 10923-16383

现在要存储一个键为 user:1001 的数据。

计算哈希槽
  1. 使用 CRC16-CCITT 哈希算法计算 user:1001 的 CRC16 值。
  2. 假设计算结果为 12345。
  3. 然后,计算该值对应的哈希槽:
    • 哈希槽 = 12345 % 16384 = 12345。
确定目标节点
  • 12345 落在 Node3 的负责范围(10923-16383),因此,user:1001 会被存储在 Node3 中。

Redis 集群中请求 key 示例(客户端直接连接的并不是对应 key 的节点)

如果客户端连接的是集群的 Node1,但需要访问存储在 Node3 的键 user:1001,查询过程如下:

查询过程

1)计算哈希槽

  • 客户端使用 CRC16-CCITT 算法计算 user:1001 的哈希值(假设为 12345)。
  • 计算哈希槽:12345 % 16384 = 12345。

2)查询请求

  • 因为客户端连接的是集群中的 Node1,所以客户端发送查询命令 GET user:1001 到 Node1。

3)Node1 响应

  • Node1 检测到请求的键 user:1001 属于 Node3,返回一个 MOVED 错误,指示客户端请求的键在另一个节点上。MOVED 错误中会返回目标节点的信息(例如,Node3 的 IP 和端口)。

4)客户端处理

  • 现代 Redis 客户端会缓存此槽位映射信息,后续对该槽的请求会直接发送到正确的节点,避免重定向。
  • 第一次收到 MOVED 错误时,客户端会连接到目标节点并更新槽位映射缓存。

5)发送查询请求到正确节点

  • 客户端向 Node3 发送 GET user:1001

6)获取结果

  • Node3 查询到 user:1001 的值(假设为 {"name": "stormsha", "age": 16}),并返回结果。

为什么 Redis 哈希槽节点的数目是 16384 呢?

1)首先是消息大小的考虑

正常的心跳包需要带上节点完整配置数据,心跳还是比较频繁的,所以需要考虑数据包的大小,如果使用 16384 数据包只要约 2KB,如果用了 65536 则需要约 8KB。

实际上槽位信息使用一个长度为 16384 位的位图(bitmap)来表示,节点拥有哪个槽位,就将对应位置的位设置为 1,否则为 0。

Redis 心跳消息(CLUSTERMSG)数据结构如下所示:

typedef struct {
    char sig[4];              /* 信息标识 */
    uint32_t totlen;          /* 消息总长度 */
    uint16_t ver;             /* 协议版本 */
    uint16_t type;            /* 消息类型, 用于区分meet,ping,pong等消息 */
    uint16_t count;           /* 消息体包含的节点数量, 仅用于meet,ping,pong消息类型*/
    uint64_t currentEpoch;    /* 当前发送节点的配置纪元 */
    uint64_t configEpoch;     /* 主节点/从节点的主的配置纪元 */
    char sender[CLUSTER_NAMELEN]; /* 发送节点的nodeId */
    unsigned char myslots[CLUSTER_SLOTS/8]; /* 发送节点负责的槽信息 */
    char slaveOf[CLUSTER_NAMELEN]; /* 如果是从节点,记录主节点的nodeId */
    uint16_t port;            /* 端口号 */
    uint16_t flags;           /* 发送节点标识,区分主从角色,是否下线等 */
    unsigned char state;      /* 发送节点的集群状态 */
    unsigned char mflags[3];  /* 消息标识 */
    union clusterMsgData data /* 消息正文 */;
} clusterMsg;

这里我们看到一个重点,即在消息头中最占空间的是 myslots[CLUSTER_SLOTS/8]

  • 当槽位为 65536 时,这部分大小: 65536÷8=8192 字节 ≈8KB
  • 当槽位为 16384 时,这部分大小: 16384÷8=2048 字节 ≈2KB

如果槽位为 65536,这个心跳消息的头部就太大了,在高频率通信的集群环境中会显著增加网络负担。

2)集群规模的考虑

根据 Redis 作者的说明,集群不太可能会扩展超过 1000 个节点,而每个主节点应该管理相当数量的哈希槽。如果使用 16384 个槽位:

  • 100 个节点集群:平均每个节点管理约 164 个槽位
  • 1000 个节点集群:平均每个节点管理约 16 个槽位

这个数量既能满足大多数集群规模需求,又不会因为槽位过少而影响数据分布均衡性。

3)内存占用的平衡

Redis 作为内存数据库,对内存使用的效率非常关注。16384 个槽位的设计在集群功能和资源消耗之间取得了很好的平衡:足够多的槽位保证良好的数据分布,同时槽位映射的内存占用和网络传输开销都保持在合理范围内。

好的,以下是测试题目解析部分分开的版本:


✅ Redis 集群选择题(共 4 题)

1. Redis 集群采用什么机制来分配数据到不同的节点? ( )
A. 哈希槽机制
B. 主备机制
C. 哈希链机制
D. 分段机制

2. Redis 集群中有多少个哈希槽? ( )
A. 2048
B. 8192
C. 16384
D. 32768

3. Redis 集群中的节点之间使用什么协议进行状态同步和故障检测? ( )
A. TCP/IP 协议
B. HTTP 协议
C. UDP 协议
D. Gossip 协议

4. 在 Redis 集群中,当客户端请求的键值对不在连接的节点上时,返回的错误类型是什么? ( )
A. ERROR
B. MOVED
C. NOT_FOUND
D. FORBIDDEN

📘 答案与解析

1. 正确答案:A
解析:Redis 集群通过哈希槽机制进行数据分配,避免单点瓶颈,将键映射到 16384 个槽中,再分配到不同节点。


2. 正确答案:C
解析:Redis 集群采用固定的 16384 个哈希槽,这个值是 2¹⁴,设计用于在带宽与灵活性之间做出平衡。


3. 正确答案:D
解析:Redis 集群使用 Gossip 协议实现去中心化状态同步和故障检测,各节点定期交换信息。


4. 正确答案:B
解析:当客户端请求的 key 不在当前节点时,Redis 返回 MOVED 重定向错误,引导客户端访问正确节点。


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

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

相关文章

浏览器多开

使用浏览器的用户功能,创建多个用户即可完成浏览器多开的需求,插件等相对独立 需要命名 然后就可以通过多个用户切换来实现多开了,不同任务选择不同用户

Redis常用数据结构和应用场景

一、前言 Redis提供了多种数据结构,每种结构对应不同的应用场景。本文对部分常用的核心数据结构和典型使用场景作出介绍。 二、String(字符串) 特点:二进制安全,可存储文本、数字、序列化对象等。场景: 缓…

【转载翻译】使用Open3D和Python进行点云处理

转自个人博客:【转载翻译】使用Open3D和Python进行点云处理 转载自:Point Cloud Processing with Open3D and Python 本文由 Carlos Melo 发布于2024年2月12日 本文很适合初学者对三维处理、点云处理以及Open3D库进行初步了解 另外,本文是基于…

用户登录不上linux服务器

一般出现这种问题,重新用root用户修改lsy用户的密码即可登录,但是当修改了还是登录不了的时候,去修改一个文件用root才能修改, 然后在最后添加上改用户的名字,例如 原本是只有user的,现在我加上了lsy了&a…

matplotlib数据展示

目录 一、绘制直方图 1、简单直方图 2、绘制横向直方图 3、绘制堆叠直方图 4、对比直方图 二、折线图与散点图 三、绘制饼图 四、雷达图 1、简单雷达图 2、多层雷达图 五、总和 在前面的学习中,我们能够使用一些库进行数据的整合,收集&#x…

MySQL 面经

1、什么是 MySQL? MySQL 是一个开源的关系型数据库,现在隶属于 Oracle 公司。是我们国内使用频率最高的一种数据库,我本地安装的是比较新的 8.0 版本。 1.1 怎么删除/创建一张表? 可以使用 DROP TABLE 来删除表,使用…

贪心算法 day08(加油站+单调递增的数字+坏了的计算机)

目录 1.加油站 2.单调递增的数字 3.坏了的计算器 1.加油站 链接:. - 力扣(LeetCode) 思路: gas[index] - cost[index],ret 表示的是在i位置开始循环时剩余的油量 a到达的最大路径假设是f那么我们可以得出 a b …

String类基本使用

文章目录 1. String类的理解和创建对象2. 创建String对象的两种方式3. 两种创建String对象的区别4. 测试5. 字符串的特性6. String 类的常见方法 1. String类的理解和创建对象 String 对象用于保存字符串,也就是一组字符序列字符串常量对象是用双引号括起的字符序列…

华为机试—火车进站

题目 火车站一共有 n 辆火车需要入站,每辆火车有一个编号,编号为 1 到 n。 同时,也有火车需要出站,由于火车站进出共享一个轨道,所以后入站的火车需要先出站。换句话说,对于某一辆火车,只有在它…

Python数组(array)学习之旅:数据结构的奇妙冒险

Python数组学习之旅:数据结构的奇妙冒险 第一天:初识数组的惊喜 阳光透过窗帘缝隙洒进李明的房间,照亮了他桌上摊开的笔记本和笔记本电脑。作为一名刚刚转行的金融分析师,李明已经坚持学习Python编程一个月了。他的眼睛因为昨晚熬夜编程而微微发红,但脸上却挂着期待的微…

spark-core编程2

Key-Value类型: foldByKey 当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey combineByKey 最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。类似于aggregate()&…

AIDD-人工智能药物设计-大语言模型在医学领域的革命性应用

Nat. Rev. Bioeng. | 大语言模型在医学领域的革命性应用 大型语言模型(LLMs),如 ChatGPT,因其对人类语言的理解与生成能力而备受关注。尽管越来越多研究探索其在临床诊断辅助、医学教育等任务中的应用,但关于其发展、…

Windows 系统中安装 Git 并配置 GitHub 账户

由于电脑重装系统,重新配置了git. 以下是在 Windows 系统中安装 Git 并配置 GitHub 账户的详细步骤: 1. 安装 Git 访问 Git 官网下载页面下载 Windows 版本的 Git 安装程序运行安装程序,使用默认选项即可 2. 配置 Git 用户信息 打开命令…

QQ风格客服聊天窗口

QQ风格客服聊天窗口 展示引入方式 展示 引入方式 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title&g…

MCU刷写——Hex文件格式详解及Python代码

工作之余来写写关于MCU的Bootloader刷写的相关知识,以免忘记。今天就来聊聊Hex这种文件的格式,我是分享人M哥,目前从事车载控制器的软件开发及测试工作。 学习过程中如有任何疑问,可底下评论! 如果觉得文章内容在工作学习中有帮助到你,麻烦点赞收藏评论+关注走一波!感谢…

汇舟问卷:国外问卷调查技巧有哪些,具体该怎么操作

大家好&#xff0c;我是汇舟问卷&#xff0c;今天咱们就聊聊国外问卷答题的技巧和操作步骤&#xff0c;保你听完立马能上手&#xff01; 一、答题前先创建人设 1&#xff0c;进题时先瞄两眼问题&#xff0c;快速判断问卷主题&#xff0c;再定人设。比如遇到奶粉问卷&#xff…

C++标识符:检查是否和保留字冲突

1. 基础知识 最基本的要求&#xff1a; 字母、数字、下划线组成&#xff0c; 并且不能是数字开头。 禁忌1&#xff1a; C 关键字不能用做标识符。 它们是&#xff1a; alignas alignof asm auto bool break case catch char char16_t char32_t class const constexpr const_…

《Python星球日记》第27天:Seaborn 可视化

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 专栏&#xff1a;《Python星球日记》&#xff0c;限时特价订阅中ing 目录 一、Seabor…

自动驾驶技术-相机_IMU时空标定

自动驾驶技术-相机_IMU时空标定 时间延迟 时间延迟 参考链接1、2 相机主要分为全局和卷帘快门相机&#xff0c;从触发到成像的过程包括&#xff1a;复位时间、AE()曝光时间、读出时间 全局快门如下图所示 卷帘快门如下图所示 相机录制视频时&#xff0c;为了保持固定频率&am…

”插入排序“”选择排序“

文章目录 插入排序1. 直接插入排序(O(n^2))举例1&#xff1a;举例2&#xff1a;直插排序的"代码"直插排序的“时间复杂度” 2. 希尔排序(O(n^1.3))方法一方法二(时间复杂度更优) 选择排序堆排序直接选择排序 我们学过冒泡排序&#xff0c;堆排序等等。&#xff08;回…