Redis 的高可用和高并发能力依赖于合理的集群架构设计。本文深入解析主从复制、哨兵模式和 Cluster 集群的原理与最佳实践。
一、Redis 集群架构演进¶
| 架构 | 特点 | 适用场景 | 数据分片 | 自动故障转移 |
|---|---|---|---|---|
| 单机 | 简单,但无高可用保障 | 开发/测试环境 | ❌ | ❌ |
| 主从复制 | 读写分离,手动故障转移 | 小规模,读多写少 | ❌ | ❌ |
| 哨兵模式 | 自动故障转移,监控主从状态 | 中等规模,需高可用 | ❌ | ✅ |
| Cluster | 数据分片,水平扩展,去中心化 | 大规模,高并发,需分片 | ✅ | ✅ |
二、主从复制 (Replication)¶
2.1 核心原理¶
主从复制: 将主节点(Master)的数据复制到从节点(Slave),实现读写分离。
┌─────────┐ 写操作
│ Master │ ◄────── 客户端
└────┬────┘
│ 数据同步
├──────────┬──────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Slave 1 │ │ Slave 2 │ │ Slave 3 │ ◄── 读操作
└─────────┘ └─────────┘ └─────────┘
2.2 复制流程¶
第一阶段:建立连接¶
- 从节点执行
REPLICAOF <masterip> <masterport>(或配置replicaof)。 - 从节点与主节点建立 TCP 连接。
- 从节点发送
PING命令,主节点回复PONG(验证连通性)。
第二阶段:全量同步(Full Resync)¶
触发场景: 首次同步或从节点断线时间过长(增量缓冲区溢出)。
流程:
1. 从节点发送 PSYNC <runid> <offset>
│
2. 主节点执行 BGSAVE 生成 RDB 快照
│
3. 主节点将 RDB 文件发送给从节点
│
4. 从节点清空旧数据,加载 RDB 文件
│
5. 主节点将 BGSAVE 期间的新命令发送给从节点(backlog)
关键参数:
- runid: 主节点的唯一标识(每次重启会变化)。
- offset: 复制偏移量(标记同步进度)。
第三阶段:增量同步(Partial Resync)¶
触发场景: 从节点短暂断线重连。
流程:
1. 从节点发送 PSYNC <runid> <offset>
│
2. 主节点检查复制积压缓冲区(repl-backlog)
│
3. 如果 offset 在缓冲区范围内,发送增量命令
│
4. 如果不在,退化为全量同步
复制积压缓冲区(repl-backlog):
- 环形缓冲区,默认 1MB。
- 存储最近的写命令,用于断线重连时的增量同步。
配置:
repl-backlog-size 1mb # 缓冲区大小
repl-backlog-ttl 3600 # 无从节点时,缓冲区保留时间(秒)
第四阶段:命令传播(Command Propagation)¶
正常运行时: 主节点将每条写命令实时传播给所有从节点。
心跳机制:
- 从节点每秒发送一次
REPLCONF ACK <offset>(确认同步进度)。 - 主节点检测从节点是否在线(超时则标记为下线)。
2.3 主从复制配置¶
从节点配置¶
# redis.conf(从节点)
replicaof 192.168.1.100 6379 # 指定主节点地址
masterauth mypassword # 主节点密码(如果有)
# 从节点只读(默认)
replica-read-only yes
# 从节点优先级(数值越小优先级越高,0 表示永不晋升为主节点)
replica-priority 100
主节点配置¶
# redis.conf(主节点)
# 最少 N 个从节点在线,主节点才接受写操作
min-replicas-to-write 1
# 从节点延迟超过 N 秒,主节点停止写操作
min-replicas-max-lag 10
2.4 主从复制的问题¶
| 问题 | 影响 | 解决方案 |
|---|---|---|
| 主节点宕机 | 需手动切换,服务中断 | 使用哨兵模式 |
| 全量同步阻塞 | BGSAVE 导致性能抖动 | 使用 SSD、控制主节点数据量 |
| 从节点数量过多 | 主节点网络和 CPU 压力增大 | 使用级联复制(从-从结构) |
| 复制延迟 | 从节点数据可能过期 | 监控 info replication 中的 lag |
三、哨兵模式 (Sentinel)¶
3.1 核心功能¶
哨兵(Sentinel) 是 Redis 的高可用解决方案,提供以下功能:
- 监控(Monitoring): 检查主从节点是否正常工作。
- 通知(Notification): 通过 API 或脚本通知管理员。
- 自动故障转移(Automatic Failover): 主节点宕机时,自动选举新主节点。
- 配置中心: 客户端通过哨兵获取当前主节点地址。
3.2 架构设计¶
┌─────────────────────────────────────────┐
│ Sentinel 集群(至少 3 个) │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │Sentinel│ │Sentinel│ │Sentinel│ │
│ │ 1 │ │ 2 │ │ 3 │ │
│ └───┬───┘ └───┬───┘ └───┬───┘ │
│ │ │ │ │
└──────┼──────────┼──────────┼────────────┘
│ │ │
└──────────┼──────────┘
│
┌────────▼────────┐
│ Master (主节点) │
└────────┬────────┘
│
┌───────┴───────┐
▼ ▼
┌────────┐ ┌────────┐
│ Slave 1 │ │ Slave 2 │
└────────┘ └────────┘
3.3 故障检测与选举¶
主观下线(Subjectively Down, SDOWN)¶
定义: 单个哨兵认为主节点下线(PING 超时)。
配置:
# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 2 # 监控主节点
sentinel down-after-milliseconds mymaster 5000 # 5 秒超时判定下线
客观下线(Objectively Down, ODOWN)¶
定义: 多数哨兵(quorum)认为主节点下线。
quorum 配置:
sentinel monitor mymaster 192.168.1.100 6379 2
# 2 表示至少 2 个哨兵同意,主节点才被判定为客观下线
计算公式:
quorum = 哨兵总数 / 2 + 1(推荐配置)
选举新主节点(Leader Election)¶
流程:
1. 哨兵之间通过 Raft 协议选举出一个 Leader Sentinel
│
2. Leader Sentinel 从从节点中选出新主节点
│
选举规则(优先级递减):
├─ replica-priority 最小(非 0)
├─ 复制偏移量(offset)最大(数据最新)
└─ runid 最小(字典序)
│
3. Leader Sentinel 向新主节点发送 SLAVEOF NO ONE(晋升为主节点)
│
4. 其他从节点执行 REPLICAOF <new_master>(指向新主节点)
│
5. 更新哨兵配置,通知客户端新主节点地址
3.4 哨兵配置详解¶
# sentinel.conf
# 监控主节点(名称、IP、端口、quorum)
sentinel monitor mymaster 192.168.1.100 6379 2
# 主节点密码
sentinel auth-pass mymaster mypassword
# 判定下线的超时时间(毫秒)
sentinel down-after-milliseconds mymaster 5000
# 故障转移超时时间(毫秒)
sentinel failover-timeout mymaster 180000
# 同步新主节点的从节点数量(1 表示逐个同步,避免雪崩)
sentinel parallel-syncs mymaster 1
# 脚本通知(故障转移时执行)
sentinel notification-script mymaster /var/redis/notify.sh
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
3.5 哨兵模式的优缺点¶
| 优点 | 缺点 |
|---|---|
| 自动故障转移,无需人工干预 | 写能力受限于单个主节点 |
| 监控主从节点健康状态 | 不支持数据分片(无法水平扩展) |
| 客户端自动发现新主节点 | 配置复杂,运维成本高 |
| 高可用性(至少 3 个哨兵) | 哨兵本身需要高可用(至少 3 个) |
四、Redis Cluster (集群)¶
4.1 核心特性¶
Redis Cluster 是 Redis 官方的分布式解决方案,提供:
- 数据分片(Sharding): 数据自动分散到多个节点。
- 高可用(HA): 节点宕机自动故障转移。
- 去中心化: 无 Proxy 层,节点间直接通信。
- 线性扩展: 支持动态添加/删除节点。
4.2 哈希槽(Hash Slot)机制¶
核心概念: Redis Cluster 将数据空间划分为 16384 个哈希槽(0-16383)。
槽位分配¶
示例:3 主节点集群
Master A:槽 0-5460
Master B:槽 5461-10922
Master C:槽 10923-16383
键到槽位的映射¶
计算公式:
HASH_SLOT = CRC16(key) mod 16384
示例:
key1 → CRC16("key1") = 12345 → 12345 % 16384 = 12345 → Master C
Hash Tag(哈希标签)¶
作用: 强制多个 key 分配到同一槽位(支持多键操作)。
语法: {hashtag}key
示例:
MSET {user:1000}:name "Alice" {user:1000}:age 25
# {user:1000} 是 Hash Tag,确保两个 key 在同一槽位
4.3 节点间通信(Gossip 协议)¶
Gossip 协议: 节点间通过周期性的消息交换同步集群状态。
消息类型¶
| 消息类型 | 作用 |
|---|---|
MEET |
新节点加入集群 |
PING |
心跳检测(每秒随机向几个节点发送) |
PONG |
回复 PING |
FAIL |
标记节点下线 |
PUBLISH |
发布订阅消息 |
集群总线端口¶
端口: Redis 集群使用 Redis端口 + 10000 作为集群总线端口。
示例:
节点 1:6379(客户端)、16379(集群总线)
节点 2:6380(客户端)、16380(集群总线)
4.4 故障检测与转移¶
主观下线(PFAIL)¶
定义: 节点 A 认为节点 B 下线(PING 超时)。
客观下线(FAIL)¶
定义: 多数主节点认为节点 B 下线。
流程:
1. 节点 A 发现节点 B 超时(PFAIL)
│
2. 节点 A 向其他节点广播:B 可能下线
│
3. 如果 超过半数主节点 同意,B 被标记为 FAIL
│
4. B 的从节点发起选举,晋升为新主节点
4.5 搭建 Redis Cluster¶
最小配置(3 主 3 从)¶
Master A (6379) ──┬── Slave A1 (6382)
Master B (6380) ──┼── Slave B1 (6383)
Master C (6381) ──┴── Slave C1 (6384)
配置文件¶
# redis-6379.conf
port 6379
cluster-enabled yes # 启用集群模式
cluster-config-file nodes-6379.conf # 集群配置文件
cluster-node-timeout 5000 # 节点超时时间(毫秒)
appendonly yes
创建集群¶
redis-cli --cluster create \
127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 \
127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 \
--cluster-replicas 1
参数说明:
--cluster-replicas 1:每个主节点有 1 个从节点。
4.6 集群扩容与缩容¶
扩容(添加节点)¶
# 1. 启动新节点
redis-server redis-6385.conf
# 2. 将新节点加入集群
redis-cli --cluster add-node 127.0.0.1:6385 127.0.0.1:6379
# 3. 重新分配槽位
redis-cli --cluster reshard 127.0.0.1:6379
缩容(删除节点)¶
# 1. 迁移槽位到其他节点
redis-cli --cluster reshard 127.0.0.1:6379
# 2. 删除节点
redis-cli --cluster del-node 127.0.0.1:6379 <node-id>
4.7 客户端路由(MOVED 与 ASK)¶
MOVED 重定向¶
场景: 客户端访问错误节点。
流程:
Client → Node A (槽 1234 不在此节点)
Node A 返回:MOVED 1234 127.0.0.1:6380
Client → Node B (正确节点)
ASK 重定向¶
场景: 槽位正在迁移中。
流程:
Client → Node A (槽 1234 正在迁移到 Node B)
Node A 返回:ASK 1234 127.0.0.1:6380
Client → Node B 发送 ASKING 命令
Client → Node B 执行操作
4.8 Cluster 的限制¶
| 限制 | 说明 | 解决方案 |
|---|---|---|
| 不支持多键操作 | 如 MGET key1 key2(不同槽位) |
使用 Hash Tag |
| 不支持多数据库 | Cluster 模式只有 db0 |
业务层隔离 |
| 事务支持有限 | 事务中的 key 必须在同一槽位 | 使用 Hash Tag 或 Lua 脚本 |
| 不支持发布订阅的跨节点 | PUBLISH 只在当前节点生效 |
使用消息队列(Kafka、RabbitMQ) |
| 从节点不参与读操作(默认) | 从节点仅用于高可用 | 开启 READONLY 模式 |
4.9 Cluster vs 哨兵对比¶
| 特性 | 哨兵模式 | Cluster |
|---|---|---|
| 数据分片 | ❌(单主节点,写能力受限) | ✅(16384 个槽位,水平扩展) |
| 自动故障转移 | ✅ | ✅ |
| 配置复杂度 | 中等 | 高 |
| 客户端支持 | 需哨兵感知客户端 | 需集群感知客户端(如 Jedis、Lettuce) |
| 适用场景 | 中等规模,读多写少 | 大规模,高并发,需分片 |
五、集群选型建议¶
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 小规模,读多写少 | 主从复制 + 哨兵 | 简单,成本低 |
| 中等规模,需高可用 | 哨兵模式 | 自动故障转移,配置相对简单 |
| 大规模,高并发,需分片 | Redis Cluster | 水平扩展,数据分片,去中心化 |
| 超大规模(TB 级数据) | Cluster + Proxy | Twemproxy/Codis 实现分片路由 |
| 多云/混合云部署 | 哨兵 + VIP | VIP 漂移实现跨机房故障转移 |
六、面试高频问题¶
- 主从复制的全量同步和增量同步有什么区别?
-
全量同步:RDB + backlog;增量同步:仅发送断线期间的命令。
-
哨兵是如何选举出新主节点的?
-
Raft 协议选举 Leader Sentinel,再根据优先级、offset、runid 选出新主节点。
-
Redis Cluster 如何实现数据分片?
-
通过 CRC16(key) % 16384 计算槽位,每个节点负责一部分槽位。
-
Cluster 的 MOVED 和 ASK 重定向有什么区别?
-
MOVED:槽位固定不在此节点;ASK:槽位正在迁移中。
-
如何解决 Cluster 的多键操作限制?
-
使用 Hash Tag 强制多个 key 在同一槽位。
-
为什么哨兵至少需要 3 个?
-
保证 quorum(多数派)有效,避免脑裂。
-
Cluster 如何保证高可用?
- 每个主节点有从节点,主节点宕机时自动故障转移。
通过合理的集群架构设计,Redis 可以支撑亿级流量和 PB 级数据!🚀