news 2026/1/29 12:29:38

【Redis】Redis集群模式架构详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Redis】Redis集群模式架构详解

Redis集群模式架构详解

前言

Redis 本质上是个单机服务,一旦崩了,服务就不可用。加几个从节点做备份可以实现高可用,但所有节点存的都是全量数据,无法突破单机内存瓶颈。如果想存储更多数据,该怎么办?这就需要 Redis 集群模式了。

🏠个人主页:你的主页


文章目录

  • Redis集群模式架构详解
    • 一、为什么需要集群模式
    • 二、数据分片方案演进
    • 三、哈希槽机制详解
    • 四、集群节点通信机制
    • 五、客户端请求路由
    • 六、集群扩缩容与数据迁移
    • 七、集群故障转移
    • 八、集群模式的限制
    • 九、总结

一、为什么需要集群模式

1.1 单机 Redis 的瓶颈

Redis 将数据存储在内存中,单机服务器的内存总有上限。假设一台服务器内存 64GB,Redis 实际可用约 50GB,当数据量超过这个限制就无法继续存储了。

1.2 主从复制的局限

主从复制可以实现高可用:

┌─────────────┐ │ Master │ ← 写入 │ (全量数据) │ └──────┬──────┘ │ 同步 ┌───────┴───────┐ ↓ ↓ ┌─────────────┐ ┌─────────────┐ │ Slave-1 │ │ Slave-2 │ ← 读取 │ (全量数据) │ │ (全量数据) │ └─────────────┘ └─────────────┘

问题:从节点存的是和主节点一样的全量数据,加再多从节点也无法突破单机内存瓶颈。

打个比方:你有一本 500 页的书,复印了 10 份分给 10 个人。虽然有 10 个人可以同时读这本书(读性能提升),但每个人手里还是完整的 500 页,书的内容并没有变多。

1.3 集群模式的思路

既然单机内存有限,那就把数据切分成多份,放到多个 Redis 节点上:

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Node-A │ │ Node-B │ │ Node-C │ │ 数据分片1 │ │ 数据分片2 │ │ 数据分片3 │ └─────────────┘ └─────────────┘ └─────────────┘

打个比方:把 500 页的书拆成 3 本,A 拿 1-170 页,B 拿 171-340 页,C 拿 341-500 页。这样每个人只需要保管一部分内容,总容量就扩大了 3 倍。


二、数据分片方案演进

2.1 方案一:取模分片

最直观的想法:对 Key 进行哈希,然后对节点数取模。

节点编号 = hash(key) % 节点总数

举个例子:假设有 3 个 Redis 节点

hash("order:1001") % 3 = 1 → 存到 Node-1 hash("order:1002") % 3 = 2 → 存到 Node-2 hash("order:1003") % 3 = 0 → 存到 Node-0

致命问题:扩容时数据大规模迁移

假设现在要从 3 个节点扩容到 4 个节点:

# 扩容前 hash("order:1001") % 3 = 1 → Node-1 hash("order:1002") % 3 = 2 → Node-2 hash("order:1003") % 3 = 0 → Node-0 # 扩容后(节点数变成4) hash("order:1001") % 4 = 2 → Node-2 ← 需要迁移! hash("order:1002") % 4 = 3 → Node-3 ← 需要迁移! hash("order:1003") % 4 = 1 → Node-1 ← 需要迁移!

几乎所有数据都需要重新分配,迁移成本极高。

2.2 方案二:一致性哈希

一致性哈希将整个哈希值空间组织成一个虚拟的圆环:

0 │ Node-C ──┼── Node-A ╱│╲ ╱ │ ╲ ╱ │ ╲ ╱ │ ╲ ╱ │ ╲ ╱ │ ╲ Node-B ──┴──────

数据的 Key 经过哈希后落在环上某个位置,然后顺时针找到第一个节点,就是它应该存储的位置。

优点:扩容时只影响相邻节点的数据

缺点

  • 节点少时数据分布不均匀(数据倾斜)
  • 需要引入虚拟节点来解决,增加了复杂度

2.3 方案三:哈希槽(Redis Cluster 采用)

Redis Cluster 没有使用一致性哈希,而是引入了**哈希槽(Hash Slot)**的概念。

核心思想:在 Key 和 Node 之间加一层固定长度的中间层。

Key → 哈希槽(固定 16384 个) → Node

为什么这样设计?

没有什么是加一层中间层不能解决的。既然担心节点数变化导致分片结果改变,那就让公式里用于计算的值固定下来

槽位编号 = CRC16(key) % 16384

不管节点数怎么变,16384 这个数字永远不变,所以同一个 Key 计算出的槽位编号也永远不变。


三、哈希槽机制详解

3.1 哈希槽分配

Redis Cluster 固定有16384个哈希槽(编号 0-16383),这些槽位会分配给集群中的各个主节点。

举个例子:3 个主节点的集群

Node-A: 负责槽位 0 ~ 5460 (5461 个槽) Node-B: 负责槽位 5461 ~ 10922 (5462 个槽) Node-C: 负责槽位 10923 ~ 16383 (5461 个槽)

3.2 Key 到槽位的映射

当客户端要操作一个 Key 时:

1. 计算 Key 的 CRC16 值 2. 对 16384 取模,得到槽位编号 3. 找到负责这个槽位的节点 4. 向该节点发送命令

举个例子

Key = "product:10086" CRC16("product:10086") = 28456 槽位编号 = 28456 % 16384 = 12072 12072 在 10923~16383 范围内 → 由 Node-C 负责

3.3 为什么是 16384 个槽

这是一个权衡的结果:

槽位太少

  • 数据分布不够均匀
  • 扩容时迁移粒度太粗

槽位太多

  • 每个节点需要维护的槽位信息太多
  • 节点间心跳包携带的数据量太大

16384 = 2^14,是一个比较合适的数字:

  • 心跳包中槽位信息只需要 2KB(16384 / 8 = 2048 字节)
  • 即使集群有 1000 个节点,每个节点平均也能分到 16 个槽

3.4 Hash Tag:控制 Key 的槽位分配

有时候我们希望某些相关的 Key 落在同一个槽位(同一个节点),可以使用Hash Tag

语法:Key 中用{}包裹的部分会被用来计算槽位

# 这三个 Key 会落在同一个槽位 {user:1001}:name {user:1001}:age {user:1001}:email # 因为它们的 Hash Tag 都是 "user:1001" 槽位 = CRC16("user:1001") % 16384

使用场景

  • 需要对多个 Key 执行事务操作
  • 需要使用 Lua 脚本操作多个 Key
  • 需要使用 MGET/MSET 批量操作

四、集群节点通信机制

4.1 Gossip 协议

Redis Cluster 节点之间使用Gossip 协议进行通信,这是一种去中心化的协议。

特点

  • 没有中心节点,每个节点地位平等
  • 节点之间互相交换信息,最终达到一致
  • 类似"八卦传播",信息会逐渐扩散到所有节点
Node-A 发现 Node-D 挂了 ↓ 告诉 Node-B Node-B 知道了 ↓ 告诉 Node-C Node-C 知道了 ↓ 最终所有节点都知道 Node-D 挂了

4.2 节点间通信内容

每个节点会维护以下信息:

信息类型说明
节点信息集群中所有节点的 IP、端口、状态
槽位分配每个槽位由哪个节点负责
故障信息哪些节点被标记为故障

4.3 PING/PONG 心跳

节点之间通过 PING/PONG 消息保持通信:

Node-A → PING → Node-B Node-B → PONG → Node-A

心跳消息中携带:

  • 发送节点的信息
  • 发送节点知道的部分其他节点信息(随机选择)
  • 槽位分配信息

通过不断交换信息,所有节点最终会达成一致的集群视图。


五、客户端请求路由

5.1 客户端如何知道数据在哪

客户端首次连接集群时,会通过CLUSTER SLOTS命令获取槽位分配信息:

127.0.0.1:7000>CLUSTER SLOTS1)1)(integer)02)(integer)54603)1)"192.168.1.101"2)(integer)70002)1)(integer)54612)(integer)109223)1)"192.168.1.102"2)(integer)70013)1)(integer)109232)(integer)163833)1)"192.168.1.103"2)(integer)7002

客户端会在本地缓存这份槽位映射表,后续请求直接根据 Key 计算槽位,找到对应节点。

5.2 MOVED 重定向

如果客户端的槽位信息过期了,访问了错误的节点,会收到MOVED错误:

# 客户端访问 Node-A,但 Key 实际在 Node-C127.0.0.1:7000>GET product:10086(error)MOVED12072192.168.1.103:7002

MOVED 的含义

  • 12072:这个 Key 对应的槽位编号
  • 192.168.1.103:7002:负责这个槽位的节点地址

客户端收到 MOVED 后会:

  1. 更新本地的槽位映射表
  2. 重新向正确的节点发送请求

打个比方:你去政务大厅办事,走错了窗口,工作人员告诉你"这个业务请去 3 号窗口",你就去 3 号窗口,下次再办同样的事就直接去 3 号窗口了。

5.3 ASK 重定向

ASK是另一种重定向,发生在数据迁移过程中

127.0.0.1:7000>GET product:10086(error)ASK12072192.168.1.103:7002

ASK 与 MOVED 的区别

类型含义客户端行为
MOVED槽位已经永久迁移到新节点更新本地缓存,后续请求直接访问新节点
ASK槽位正在迁移中,这个 Key 可能在新节点不更新缓存,只是这一次去新节点查询

ASK 的处理流程

1. 客户端访问 Node-A,查询 Key 2. Node-A 发现这个槽位正在迁移,且本地没有这个 Key 3. Node-A 返回 ASK 重定向 4. 客户端向 Node-C 发送 ASKING 命令 5. 客户端向 Node-C 发送 GET 命令 6. Node-C 返回数据(如果有的话)

六、集群扩缩容与数据迁移

6.1 扩容流程

假设原来有 3 个节点,现在要加入第 4 个节点 Node-D。

步骤一:将新节点加入集群

redis-cli --cluster add-node192.168.1.104:7003192.168.1.101:7000

此时 Node-D 已经加入集群,但还没有分配任何槽位。

步骤二:重新分配槽位

redis-cli --cluster reshard192.168.1.101:7000

系统会询问:

  • 要迁移多少个槽位?(比如 4096 个)
  • 迁移到哪个节点?(Node-D 的 ID)
  • 从哪些节点迁移?(可以选择 all,从所有节点平均迁移)

迁移后的槽位分布

# 迁移前 Node-A: 0 ~ 5460 (5461 个槽) Node-B: 5461 ~ 10922 (5462 个槽) Node-C: 10923 ~ 16383 (5461 个槽) # 迁移后(每个节点迁移约 1365 个槽给 Node-D) Node-A: 1365 ~ 5460 (4096 个槽) Node-B: 6826 ~ 10922 (4097 个槽) Node-C: 12288 ~ 16383 (4096 个槽) Node-D: 0~1364, 5461~6825, 10923~12287 (4096 个槽)

6.2 数据迁移过程

槽位迁移时,数据是怎么迁移的?

源节点 Node-A 目标节点 Node-D ┌─────────────┐ ┌─────────────┐ │ 槽位 0~1364 │ ──── 迁移 ────→ │ 槽位 0~1364 │ │ (迁移中) │ │ (接收中) │ └─────────────┘ └─────────────┘

迁移步骤

  1. 标记槽位状态

    • 源节点将槽位标记为MIGRATING(迁出中)
    • 目标节点将槽位标记为IMPORTING(迁入中)
  2. 逐个迁移 Key

    • 获取源节点该槽位的所有 Key
    • 逐个将 Key 迁移到目标节点(MIGRATE 命令)
    • 迁移是原子操作,要么成功要么失败
  3. 更新槽位归属

    • 所有 Key 迁移完成后,更新槽位归属信息
    • 通过 Gossip 协议通知所有节点

迁移期间的请求处理

客户端请求 Key-X(属于正在迁移的槽位) ↓ 访问源节点 Node-A ↓ Node-A 检查本地是否有 Key-X ├── 有 → 直接返回 └── 没有 → 返回 ASK 重定向到 Node-D

6.3 缩容流程

缩容就是扩容的逆过程:

  1. 将要下线节点的槽位迁移到其他节点
  2. 确认槽位迁移完成
  3. 将节点从集群中移除
# 迁移槽位redis-cli --cluster reshard192.168.1.101:7000# 移除节点redis-cli --cluster del-node192.168.1.101:7000<node-id>

七、集群故障转移

7.1 主从架构

为了保证高可用,集群中的每个主节点都应该有从节点:

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Master-A │ │ Master-B │ │ Master-C │ │ 槽位 0~5460 │ │槽位5461~10922│ │槽位10923~16383│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ ↓ ↓ ↓ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Slave-A │ │ Slave-B │ │ Slave-C │ └─────────────┘ └─────────────┘ └─────────────┘

7.2 故障检测

主观下线(PFAIL)

某个节点认为另一个节点不可用。

Node-A 向 Node-B 发送 PING ↓ 超过 cluster-node-timeout 没收到 PONG ↓ Node-A 将 Node-B 标记为 PFAIL(主观下线)

客观下线(FAIL)

超过半数的主节点都认为某节点不可用。

Node-A 标记 Node-B 为 PFAIL Node-C 标记 Node-B 为 PFAIL Node-D 标记 Node-B 为 PFAIL ↓ 超过半数主节点认为 Node-B 下线 ↓ Node-B 被标记为 FAIL(客观下线) ↓ 触发故障转移

7.3 故障转移过程

当主节点被标记为 FAIL 后,它的从节点会发起选举:

1. 从节点发现主节点 FAIL 2. 从节点向其他主节点发起投票请求 3. 其他主节点投票(每个主节点只能投一票) 4. 获得超过半数票的从节点成为新主节点 5. 新主节点接管原主节点的槽位 6. 广播通知所有节点更新配置

选举优先级

  • 复制偏移量大的从节点优先(数据更完整)
  • 如果偏移量相同,节点 ID 小的优先

7.4 集群不可用的情况

以下情况会导致集群不可用:

情况说明
某个槽位没有节点负责主节点挂了且没有从节点
超过半数主节点挂了无法完成故障转移投票
集群处于 FAIL 状态需要人工介入修复

配置项cluster-require-full-coverage

  • yes(默认):任何槽位不可用,整个集群都不可用
  • no:只有不可用槽位的数据无法访问,其他正常

八、集群模式的限制

8.1 不支持跨槽位的多 Key 操作

# 如果 key1 和 key2 在不同槽位,以下命令会报错MGET key1 key2 MSET key1 value1 key2 value2

解决方案:使用 Hash Tag 让相关 Key 落在同一槽位

MGET{user:1001}:name{user:1001}:age# 可以执行

8.2 事务和 Lua 脚本的限制

事务(MULTI/EXEC)和 Lua 脚本中涉及的所有 Key 必须在同一个槽位。

# 如果 key1 和 key2 在不同槽位,事务会失败MULTI SET key1 value1 SET key2 value2 EXEC

8.3 数据库只能使用 db0

单机 Redis 支持 16 个数据库(db0-db15),但集群模式只能使用 db0。

SELECT1# 在集群模式下会报错

8.4 复制结构限制

  • 不支持多层复制(从节点的从节点)
  • 从节点只能复制主节点

8.5 Key 批量操作的性能

即使使用 Hash Tag,大量 Key 集中在一个槽位也会导致该节点压力过大(热点问题)。


九、总结

9.1 核心概念回顾

概念说明
哈希槽固定 16384 个,Key 通过 CRC16 映射到槽位
槽位分配每个主节点负责一部分槽位
MOVED槽位已永久迁移,客户端需更新缓存
ASK槽位迁移中,临时重定向
Gossip节点间去中心化通信协议
PFAIL/FAIL主观下线/客观下线

9.2 集群模式 vs 主从复制 vs 哨兵

特性主从复制哨兵模式集群模式
数据分片
自动故障转移
写入扩展
读取扩展
存储扩展

9.3 集群模式适用场景

场景是否适合
数据量超过单机内存✅ 非常适合
需要高可用✅ 适合
需要高写入吞吐✅ 适合
大量跨 Key 操作⚠️ 需要注意 Hash Tag
复杂事务操作❌ 不太适合

一句话总结:Redis 集群通过哈希槽实现数据分片,突破单机内存限制;通过主从复制实现高可用;通过 Gossip 协议实现去中心化管理。是 Redis 在大规模场景下的最佳实践方案。


热门专栏推荐

  • Agent小册
  • Java基础合集
  • Python基础合集
  • Go基础合集
  • 大数据合集
  • 前端小册
  • 数据库合集
  • Redis 合集
  • Spring 全家桶
  • 微服务全家桶
  • 数据结构与算法合集
  • 设计模式小册
  • 消息队列合集

等等等还有许多优秀的合集在主页等着大家的光顾,感谢大家的支持

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😊
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🙏
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🌟

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/20 15:23:37

毕业即就业!网络安全专业大学生必备的5大核心技能与实战指南

网络安全实战路线图&#xff1a;5大核心技能助你成为企业争抢人才&#xff08;建议收藏&#xff09; 本文揭秘网络安全人才成长的五大核心技能&#xff1a;技术根基、攻防实战、行业合规、证书背书与求职策略。强调企业急需实战型人才&#xff0c;而非仅懂理论的学生。通过&qu…

作者头像 李华
网站建设 2026/1/22 14:21:36

12、游戏开发:用户界面与人工智能实现

游戏开发:用户界面与人工智能实现 1. 用户界面元素添加 在游戏开发中,用户界面元素的添加至关重要。以下是一些关键的用户界面元素添加步骤和相关知识。 1.1 对话框初始化 在开发过程中,我们需要为对话框类定义初始化器,具体操作如下: - 添加对话框的背景图像。 - 添…

作者头像 李华
网站建设 2026/1/29 8:29:13

申请专利带来的好处

专利对大家而言应该都不陌生,专利作为科技创新的载体,目前除了一般的减税、获得必要的现金收入作用外,还具有提升个人价值、宣传企业形象的重要作用.专利带来的好处个人拥有专利的好处:1、拥有3项发明专利或实用新型专利的个人,就可以加入中国发明家协会,成为发明家.2、拥有专利…

作者头像 李华
网站建设 2026/1/29 10:16:43

BilibiliSponsorBlock智能配置:一键告别B站广告干扰

BilibiliSponsorBlock智能配置&#xff1a;一键告别B站广告干扰 【免费下载链接】BilibiliSponsorBlock 一款跳过B站视频中恰饭片段的浏览器插件&#xff0c;移植自 SponsorBlock。A browser extension to skip sponsored segments in videos on Bilibili.com, ported from the…

作者头像 李华
网站建设 2026/1/27 8:47:03

单细胞T细胞分析新突破:高效追踪免疫应答全流程

在免疫治疗研究领域&#xff0c;单细胞T细胞数据分析长期以来面临着数据维度高、追踪难度大、可视化不足等严峻挑战。传统分析方法难以应对T细胞受体克隆动态变化的复杂性&#xff0c;研究人员往往需要在多个工具间不断切换&#xff0c;大大增加了学习和使用成本。 【免费下载链…

作者头像 李华