第4篇:Redis Cluster集群深入理解

📚 文章概述

Redis Cluster是Redis官方提供的分布式解决方案,它通过数据分片、节点通信、故障转移等机制实现了高可用、高性能的分布式Redis集群。本文将深入解析Redis Cluster的架构设计、哈希槽分配机制、Gossip通信协议、数据路由原理,以及集群的扩容缩容操作,帮助读者全面理解Redis Cluster的工作原理和最佳实践。


一、理论部分

1.1 Redis Cluster概述

1.1.1 为什么需要Redis Cluster?

单机Redis的局限性:

  1. 容量限制:单机内存容量有限
  2. 性能瓶颈:单机处理能力有限
  3. 单点故障:主从复制和哨兵模式仍存在单点问题
  4. 扩展困难:无法水平扩展

Redis Cluster的优势:

  1. 水平扩展:可以动态添加节点,扩展容量和性能
  2. 高可用:自动故障转移,无单点故障
  3. 数据分片:数据自动分布到多个节点
  4. 透明路由:客户端自动路由到正确的节点
1.1.2 Redis Cluster架构

Gossip协议 Gossip协议 Gossip协议 Gossip协议 Gossip协议 Gossip协议 Gossip协议 Gossip协议 Gossip协议 客户端 Cluster节点1 Cluster节点2 Cluster节点3 Cluster节点4 Cluster节点5 Cluster节点6

架构特点:

  • 无中心化:所有节点地位平等,无中心节点
  • 数据分片:使用哈希槽(Hash Slot)分片
  • 节点通信:使用Gossip协议进行节点间通信
  • 故障转移:自动检测和转移故障节点

1.2 哈希槽(Hash Slot)机制

1.2.1 哈希槽概念

Redis Cluster将整个键空间分为16384个槽(slot),每个键通过CRC16算法计算后对16384取模,得到对应的槽号。

哈希槽分配:
键空间 CRC16算法 对16384取模 哈希槽 0-16383 分配到节点

计算公式:

复制代码
slot = CRC16(key) % 16384

为什么是16384?

  • 2^14 = 16384,便于位运算
  • 节点间心跳包可以携带完整的槽分配信息
  • 平衡了分片粒度和通信开销
1.2.2 哈希槽分配

初始分配:
16384个哈希槽 节点1: 0-5460 节点2: 5461-10922 节点3: 10923-16383

3主3从架构的槽分配:
16384个哈希槽 主节点1: 0-5460 主节点2: 5461-10922 主节点3: 10923-16383 从节点1 从节点2 从节点3

槽分配规则:

  • 每个主节点负责一部分哈希槽
  • 所有16384个槽必须被分配
  • 槽可以重新分配(扩容/缩容时)
1.2.3 键到槽的映射

单个键:

bash 复制代码
# 计算键的槽号
CLUSTER KEYSLOT mykey
# 返回: 1620

键标签(Hash Tags):

为了将相关的键分配到同一个槽,可以使用键标签:

bash 复制代码
# 格式:{tag}key
user:{1000}:name    # 槽号基于 {1000} 计算
user:{1000}:email  # 槽号基于 {1000} 计算
# 这两个键会被分配到同一个槽

键标签示例:

graph TD A[键: user:1000:name] --> B{提取标签} B -->|找到{1000}| C[计算 {1000} 的槽号] C --> D[分配到槽 1234] E[键: user:1000:email] --> F{提取标签} F -->|找到{1000}| G[计算 {1000} 的槽号] G --> H[分配到槽 1234] D --> I[同一槽] H --> I style I fill:#ccffcc

1.3 节点通信协议(Gossip)

1.3.1 Gossip协议原理

Gossip协议是一种去中心化的通信协议,节点之间通过随机选择其他节点进行信息交换。

Gossip协议特点:

  1. 去中心化:无中心节点,所有节点地位平等
  2. 随机通信:随机选择节点进行信息交换
  3. 最终一致性:信息最终会传播到所有节点
  4. 容错性强:部分节点故障不影响整体通信

Gossip通信过程:
Node1 Node2 Node3 Node4 每100ms执行一次 PING (随机选择) PONG + 集群信息 PING (随机选择) PONG + 集群信息 PING (随机选择) PONG + 集群信息 信息逐渐传播到所有节点 Node1 Node2 Node3 Node4

1.3.2 节点消息类型

1. MEET消息

  • 用于将新节点加入集群
  • 格式:CLUSTER MEET <ip> <port>

2. PING消息

  • 用于检测节点是否在线
  • 每100ms随机选择一个节点发送PING
  • 包含发送节点的集群信息

3. PONG消息

  • 响应PING或MEET消息
  • 包含节点的集群信息

4. FAIL消息

  • 节点检测到其他节点故障时广播
  • 触发故障转移

5. PUBLISH消息

  • 用于发布订阅功能

消息传播:
节点1检测到节点2故障 广播FAIL消息 节点3收到FAIL消息 节点4收到FAIL消息 节点5收到FAIL消息 更新集群状态

1.3.3 集群状态传播

集群状态信息包括:

  • 节点列表和角色(主/从)
  • 哈希槽分配信息
  • 节点故障信息
  • 配置纪元(config epoch)

状态传播过程:
节点1状态变更 通过Gossip传播 节点2更新状态 节点3更新状态 节点4更新状态 继续传播 最终所有节点状态一致

1.4 数据路由原理

1.4.1 客户端路由

路由流程:
有 无 是 否 客户端发送命令 本地缓存有槽信息? 计算键的槽号 连接任意节点 槽在当前节点? 执行命令 返回MOVED重定向 获取集群信息 更新本地缓存 客户端重定向到正确节点

1.4.2 MOVED重定向

当客户端访问错误的节点时,节点会返回MOVED错误:

bash 复制代码
MOVED <slot> <ip>:<port>

客户端处理:

  1. 解析MOVED错误
  2. 更新本地槽缓存
  3. 重定向到正确的节点
  4. 重新执行命令

MOVED重定向示例:
Client Node1 Node2 GET mykey 计算槽号: 5000 不在本节点 MOVED 5000 192.168.1.102:6379 更新槽缓存 GET mykey value Client Node1 Node2

1.4.3 ASK重定向

ASK重定向发生在集群扩容/缩容过程中,数据正在迁移时:

bash 复制代码
ASK <slot> <ip>:<port>

ASK vs MOVED:

  • MOVED:槽已永久迁移,更新缓存
  • ASK:槽正在迁移,临时重定向,不更新缓存

ASK重定向流程:
Client Node1 Node2 GET mykey 槽5000正在迁移 键可能在新节点 ASK 5000 192.168.1.102:6379 ASKING GET mykey value 不更新槽缓存 下次仍访问Node1 Client Node1 Node2

1.5 故障检测与转移

1.5.1 故障检测

检测机制:
Node1 Node2 Node3 PING PONG 标记为疑似故障(PFAIL) alt [Node2正常] [Node2无响应] loop [每1秒] 询问Node2状态 Node2也疑似故障 标记为确认故障(FAIL) 广播FAIL消息 Node1 Node2 Node3

故障判定:

  • 节点A向节点B发送PING,超过cluster-node-timeout时间无响应
  • 节点A标记节点B为PFAIL(疑似故障)
  • 节点A询问其他节点关于节点B的状态
  • 如果大多数主节点都认为节点B故障,标记为FAIL(确认故障)
1.5.2 故障转移

故障转移流程:
是 否 主节点故障 从节点检测到故障 从节点延迟随机时间 从节点发起选举 获得大多数主节点投票? 从节点提升为主节点 等待下一轮选举 接管原主节点的槽 广播PONG消息 故障转移完成

选举机制:

  1. 从节点延迟随机时间(0-1000ms),避免同时发起选举
  2. 向所有主节点请求投票
  3. 主节点只能投票给一个从节点
  4. 获得大多数主节点投票的从节点成为新主节点

配置纪元(Config Epoch):

  • 每个节点维护一个配置纪元
  • 故障转移时,新主节点的配置纪元会递增
  • 用于解决冲突,配置纪元大的节点优先

1.6 集群扩容

1.6.1 扩容流程

扩容步骤:
添加新节点 新节点加入集群 将新节点设置为从节点 迁移哈希槽到新节点 将新节点提升为主节点 扩容完成

详细流程:

  1. 添加新节点

    bash 复制代码
    # 在新节点启动Redis
    redis-server --cluster-enabled yes --cluster-config-file nodes.conf
    
    # 将新节点加入集群
    redis-cli --cluster add-node new_node:6379 existing_node:6379
  2. 迁移哈希槽

    bash 复制代码
    # 迁移槽到新节点
    redis-cli --cluster reshard existing_node:6379
    # 输入要迁移的槽数量
    # 输入目标节点ID
    # 输入源节点ID(all表示所有节点)
  3. 验证扩容

    bash 复制代码
    # 检查集群状态
    redis-cli --cluster check new_node:6379
1.6.2 槽迁移过程

迁移流程:
Client SourceNode TargetNode 开始迁移槽5000 MIGRATE key 接收数据 GET key ASK重定向 ASKING + GET key value value alt [键已迁移] [键未迁移] 迁移完成 确认迁移完成 更新槽分配 Client SourceNode TargetNode

迁移命令:

  • CLUSTER SETSLOT <slot> MIGRATING <node-id>:标记槽为迁移中
  • CLUSTER SETSLOT <slot> IMPORTING <node-id>:标记槽为导入中
  • MIGRATE <host> <port> <key> <db> <timeout>:迁移键

1.7 集群缩容

1.7.1 缩容流程

缩容步骤:
是 否 选择要移除的节点 是主节点? 迁移该节点的所有槽 直接移除 迁移完成 将节点从集群移除 缩容完成

详细流程:

  1. 迁移槽

    bash 复制代码
    # 将节点A的槽迁移到节点B
    redis-cli --cluster reshard node_a:6379
    # 输入要迁移的槽数量
    # 输入目标节点ID(节点B)
    # 输入源节点ID(节点A)
  2. 移除节点

    bash 复制代码
    # 移除节点
    redis-cli --cluster del-node node_a:6379 <node-id>
1.7.2 注意事项
  • 主节点缩容:必须先迁移所有槽
  • 从节点缩容:可以直接移除
  • 数据安全:确保数据已迁移完成
  • 集群状态:移除后检查集群状态

1.8 集群配置

1.8.1 基本配置
conf 复制代码
# 启用集群模式
cluster-enabled yes

# 集群配置文件
cluster-config-file nodes.conf

# 节点超时时间(毫秒)
cluster-node-timeout 15000

# 集群迁移相关
cluster-migration-barrier 1
cluster-require-full-coverage yes

关键配置说明:

  • cluster-enabled:必须设置为yes才能启用集群模式
  • cluster-config-file:集群配置文件,自动生成
  • cluster-node-timeout:节点超时时间,超过此时间认为节点故障
  • cluster-migration-barrier:主节点至少保留的从节点数量
  • cluster-require-full-coverage:是否要求所有槽都被覆盖
1.8.2 集群创建

方式1:使用redis-cli工具

bash 复制代码
# 创建集群(3主3从)
redis-cli --cluster create \
  192.168.1.100:6379 \
  192.168.1.101:6379 \
  192.168.1.102:6379 \
  192.168.1.103:6379 \
  192.168.1.104:6379 \
  192.168.1.105:6379 \
  --cluster-replicas 1

方式2:手动创建

bash 复制代码
# 1. 启动所有节点(启用集群模式)
redis-server --cluster-enabled yes

# 2. 节点互相MEET
redis-cli -h node1 -p 6379 CLUSTER MEET node2 6379
redis-cli -h node1 -p 6379 CLUSTER MEET node3 6379
# ... 所有节点互相MEET

# 3. 分配槽
redis-cli -h node1 -p 6379 CLUSTER ADDSLOTS {0..5460}
redis-cli -h node2 -p 6379 CLUSTER ADDSLOTS {5461..10922}
redis-cli -h node3 -p 6379 CLUSTER ADDSLOTS {10923..16383}

# 4. 设置主从关系
redis-cli -h node4 -p 6379 CLUSTER REPLICATE <node1-id>
redis-cli -h node5 -p 6379 CLUSTER REPLICATE <node2-id>
redis-cli -h node6 -p 6379 CLUSTER REPLICATE <node3-id>

1.9 集群限制

1.9.1 功能限制

不支持的命令:

  • 多键操作(除非使用键标签)
  • 事务(MULTI/EXEC)
  • Lua脚本(涉及多键)
  • 数据库选择(SELECT命令)

原因:

  • 这些操作需要访问多个节点
  • 集群模式下键分布在不同节点
  • 无法保证原子性
1.9.2 键标签使用

多键操作示例:

bash 复制代码
# ❌ 错误:键在不同节点
MSET user:1000:name "Alice" user:2000:name "Bob"

# ✅ 正确:使用键标签
MSET user:{1000}:name "Alice" user:{1000}:email "alice@example.com"
# 两个键都在同一个槽

键标签规则:

  • 格式:{tag}key
  • 只有{}中的部分用于计算槽号
  • 可以强制相关键在同一槽

1.10 集群监控

1.10.1 集群状态检查
bash 复制代码
# 检查集群状态
redis-cli --cluster check <node>:<port>

# 查看集群信息
redis-cli -h <node> -p <port> CLUSTER INFO

# 查看节点信息
redis-cli -h <node> -p <port> CLUSTER NODES
1.10.2 关键指标

集群健康指标:

  • cluster_state:集群状态(ok/fail)
  • cluster_slots_assigned:已分配的槽数
  • cluster_slots_ok:正常槽数
  • cluster_known_nodes:已知节点数
  • cluster_size:主节点数量

节点状态指标:

  • connected:连接的节点数
  • cluster_epoch:配置纪元
  • node_id:节点ID

二、实践指南

2.1 集群部署规划

2.1.1 节点数量规划

最小配置:

  • 3个主节点(每个主节点至少1个从节点)
  • 总共6个节点

推荐配置:

  • 根据数据量和性能需求确定主节点数量
  • 每个主节点配置1-2个从节点
  • 主节点数量建议为奇数

节点规划示例:
集群规划 小型集群: 3主3从 中型集群: 5主5从 大型集群: 10主10从 适合: 数据量<100GB 适合: 数据量100GB-500GB 适合: 数据量>500GB

2.1.2 网络规划

网络要求:

  • 所有节点之间网络互通
  • 建议使用内网,避免公网延迟
  • 节点间延迟建议<1ms

部署建议:

  • 主从节点部署在不同物理机
  • 避免主节点和从节点同时故障
  • 考虑机架、机房分布

2.2 集群运维

2.2.1 日常监控

监控指标:

  • 集群状态(cluster_state)
  • 槽分配情况
  • 节点状态
  • 复制延迟
  • 内存使用
  • 网络流量

监控工具:

  • redis-cli --cluster check
  • CLUSTER INFO
  • CLUSTER NODES
  • 第三方监控工具(Prometheus + Grafana)
2.2.2 故障处理

常见故障:

  1. 节点故障

    • 检查节点状态
    • 等待自动故障转移
    • 修复故障节点后重新加入
  2. 网络分区

    • 检查网络连通性
    • 等待网络恢复
    • 手动修复集群状态
  3. 槽未覆盖

    • 检查槽分配
    • 确保所有槽都有主节点
    • 修复槽分配
2.2.3 备份与恢复

备份策略:

  • 定期备份RDB文件
  • 备份集群配置文件
  • 记录集群拓扑信息

恢复步骤:

  1. 恢复RDB文件到各节点
  2. 恢复集群配置文件
  3. 重启集群节点
  4. 验证集群状态

三、总结

3.1 关键知识点回顾

  1. 哈希槽机制

    • 16384个槽,均匀分配到主节点
    • 使用CRC16算法计算键的槽号
    • 支持键标签强制相关键在同一槽
  2. Gossip协议

    • 去中心化通信协议
    • 节点间随机通信
    • 最终一致性
  3. 数据路由

    • 客户端计算槽号
    • MOVED/ASK重定向
    • 自动更新路由缓存
  4. 故障转移

    • 自动检测节点故障
    • 从节点选举新主节点
    • 配置纪元解决冲突
  5. 扩容缩容

    • 动态添加/移除节点
    • 槽迁移过程
    • 数据一致性保证

3.2 Cluster vs 哨兵模式对比

特性 Redis Cluster 哨兵模式
扩展性 水平扩展,支持大量节点 垂直扩展,节点数量有限
数据分片 自动分片 不分片
性能 高(多节点并行) 中(单主节点)
复杂度
适用场景 大型应用,大数据量 中小型应用
客户端支持 需要集群感知客户端 普通客户端+哨兵

3.3 最佳实践

  1. 部署建议

    • 至少3主3从,6个节点
    • 主从节点部署在不同物理机
    • 使用高速内网连接
  2. 配置建议

    • 合理设置cluster-node-timeout
    • 使用键标签优化多键操作
    • 监控集群状态和性能
  3. 运维建议

    • 定期检查集群健康状态
    • 准备扩容/缩容预案
    • 建立监控和告警机制

3.4 注意事项

  1. 功能限制

    • 不支持多键操作(除非使用键标签)
    • 不支持事务和Lua脚本(涉及多键)
    • 需要集群感知的客户端
  2. 数据一致性

    • 异步复制,存在延迟
    • 网络分区可能导致数据不一致
    • 合理设置cluster-require-full-coverage
  3. 性能考虑

    • 跨节点操作有网络开销
    • 使用键标签减少跨节点操作
    • 合理规划数据分布

下一篇预告: 第5篇将深入讲解Redis事务与Lua脚本,包括事务特性、WATCH机制、Lua脚本执行原理和原子性保证。


相关推荐
wangqiaowq3 小时前
使用 mysqldump 导出 + mysql 导入
数据库
qq_317620313 小时前
第23章-中级项目练习案例(15个)
数据库·爬虫·web开发·python项目·api开发·python案例
是三好3 小时前
SQL 性能分析及优化
android·数据库·sql
indexsunny4 小时前
互联网大厂Java面试实战:从Spring Boot到微服务的逐步深入
java·数据库·spring boot·微服务·kafka·监控·安全认证
小光学长4 小时前
ssm手工艺品交易平台4xccvou1(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring
qq_455760854 小时前
redis - 集群
数据库·redis·缓存
Li_7695324 小时前
Redis 进阶(五)—— 哨兵
数据库·redis·缓存
困知勉行19854 小时前
Redis大key处理
数据库·redis·缓存
扑火的小飞蛾4 小时前
oracle SR模板参考
数据库·oracle
搬砖的kk4 小时前
openJiuwen 快速入门:使用华为云大模型搭建 AI 智能体
数据库·人工智能·华为云