Redis 主从、哨兵、Cluster 全过程原理
Redis 高可用不能只背:
主从复制,哨兵故障转移,Cluster 分片。要真正学会,需要知道:
- 主从复制第一次同步发生了什么。
- 增量复制怎么做。
- Master 宕机时哨兵每一步怎么判断和切换。
- Cluster 为什么有 16384 个 slot。
- MOVED 和 ASK 分别代表什么。
- 主从、哨兵、Cluster 分别解决什么,不能解决什么。
三种模式先分清
| 模式 | 解决什么 | 不解决什么 |
|---|---|---|
| 主从复制 | 数据副本、读扩展 | 自动故障转移 |
| 哨兵 | 主从高可用、自动选主 | 分片容量扩展 |
| Cluster | 多 Master 分片、容量扩展、分片级故障转移 | 单 key 热点、跨 slot 多 key 限制 |
主从复制总流程
sequenceDiagram
participant S as Slave
participant M as Master
S->>M: 建立复制连接
S->>M: 发送 replconf / psync
M->>M: 判断全量还是增量
M->>S: 发送 RDB 快照或增量命令
M->>S: 持续发送写命令流
S->>S: 加载数据并重放命令从库最终通过重放主库写命令,让数据逐步接近主库。
第一次全量同步
当 Slave 第一次跟 Master 建立复制关系,通常需要全量同步。
flowchart TD
A["Slave 请求同步"] --> B["Master 生成 RDB"]
B --> C["Master 继续接收写命令"]
C --> D["写命令进入复制缓冲区"]
B --> E["RDB 发送给 Slave"]
E --> F["Slave 清空旧数据并加载 RDB"]
F --> G["Master 发送缓冲区增量命令"]
G --> H["Slave 重放增量命令"]为什么 Master 生成 RDB 时还要记录增量命令?
因为生成和传输 RDB 期间,Master 仍然可能处理写请求。如果不记录这段时间的写命令,Slave 加载 RDB 后会缺数据。
增量复制
Redis 会维护复制偏移量和复制积压缓冲区。
flowchart TD
A["Slave 断线"] --> B["记录自己复制到的 offset"]
B --> C["重新连接 Master"]
C --> D{"Master 积压缓冲区还有缺失数据吗"}
D -- "有" --> E["增量发送缺失命令"]
D -- "没有" --> F["重新全量同步"]如果断线时间短,积压缓冲区还保留缺失命令,就能增量复制。否则只能全量同步。
主从复制为什么可能丢数据
Redis 主从复制默认是异步的。
sequenceDiagram
participant C as Client
participant M as Master
participant S as Slave
C->>M: SET order:1 paid
M-->>C: 返回成功
M-->>S: 写命令还没复制过去
M--xM: Master 宕机
S->>S: 晋升为新 Master这时新 Master 可能没有刚才那条写入。
所以 Redis 不适合作为强一致账本的唯一存储。
哨兵做什么
哨兵 Sentinel 负责:
- 监控 Master 和 Slave。
- 判断 Master 是否下线。
- 多个 Sentinel 达成故障判断。
- 选择一个 Slave 晋升为新 Master。
- 通知其他 Slave 复制新 Master。
- 通知客户端新的 Master 地址。
主观下线和客观下线
| 概念 | 含义 |
|---|---|
| 主观下线 SDOWN | 一个 Sentinel 认为 Master 不可用 |
| 客观下线 ODOWN | 多个 Sentinel 达成一致,认为 Master 不可用 |
flowchart TD
A["Sentinel ping Master 失败"] --> B["标记主观下线"]
B --> C["询问其他 Sentinel"]
C --> D{"达到 quorum 吗"}
D -- "是" --> E["客观下线"]
D -- "否" --> F["继续监控"]为什么不能单个 Sentinel 说了算?
单个 Sentinel 可能只是网络抖动或自己故障。多个 Sentinel 达成一致能降低误判。
哨兵故障转移过程
flowchart TD
A["Master 客观下线"] --> B["Sentinel 选举 Leader"]
B --> C["Leader 选择一个 Slave"]
C --> D["让该 Slave 执行 slaveof no one"]
D --> E["新 Master 产生"]
E --> F["其他 Slave 改为复制新 Master"]
F --> G["通知客户端新地址"]选择 Slave 时会考虑:
- Slave 是否在线。
- 复制偏移量是否较新。
- 优先级配置。
- 延迟和健康状态。
哨兵解决不了什么
哨兵不做分片。
如果单个 Master 内存不够、写 QPS 不够,哨兵帮不了。哨兵只是在主从架构下做高可用切换。
Cluster 为什么有 slot
Redis Cluster 把整个 key 空间分成 16384 个 hash slot。
flowchart TD
A["key"] --> B["CRC16(key)"]
B --> C["对 16384 取模"]
C --> D["得到 slot"]
D --> E["slot 属于某个 Master"]为什么不用直接 key -> 节点?
slot 是中间层。迁移和扩容时移动 slot 比直接管理海量 key 到节点映射更简单。
Cluster 写入过程
flowchart TD
A["客户端 SET user:1"] --> B["计算 slot"]
B --> C["找到负责 slot 的 Master"]
C --> D["发送命令"]
D --> E["Master 写入"]
E --> F["异步复制给自己的 Replica"]成熟客户端会缓存 slot 到节点的映射,避免每次问集群。
MOVED 是什么
如果客户端访问错节点,节点会返回 MOVED:
MOVED 3999 192.168.1.10:6379含义:
这个 slot 已经稳定归属到另一个节点,你应该更新 slot 映射。flowchart TD
A["客户端访问错误节点"] --> B["节点返回 MOVED"]
B --> C["客户端更新 slot 缓存"]
C --> D["重新访问正确节点"]ASK 是什么
ASK 出现在 slot 迁移过程中。
flowchart TD
A["slot 正在从 A 迁移到 B"] --> B["客户端访问 A"]
B --> C["A 返回 ASK B"]
C --> D["客户端临时去 B 执行"]
D --> E["不立即永久更新 slot 映射"]区别:
| 重定向 | 含义 |
|---|---|
| MOVED | slot 已经稳定迁移,更新缓存 |
| ASK | slot 正在迁移,临时访问一次 |
hash tag 是什么
Cluster 多 key 操作要求 key 在同一个 slot。
hash tag 可以指定参与计算 slot 的部分:
user:{1001}:profile
user:{1001}:cart{1001} 相同,这两个 key 会落到同一个 slot。
适合:
- 同一用户多个相关 key。
- 需要 Lua 或事务处理的一组 key。
不要滥用 hash tag,否则会把大量 key 打到同一个 slot,造成热点。
Cluster 故障转移
每个 Master 通常有 Replica。Master 故障后,Replica 可以被选为新 Master。
flowchart TD
A["Master A 故障"] --> B["集群节点检测故障"]
B --> C["A 的 Replica 发起选举"]
C --> D["获得多数 Master 投票"]
D --> E["Replica 晋升为 Master"]
E --> F["接管原 slot"]Cluster 高可用仍然依赖复制。如果 Master 宕机前写入没同步给 Replica,也可能丢数据。
哨兵和 Cluster 怎么选
| 场景 | 建议 |
|---|---|
| 数据量不大,只要高可用 | 主从 + 哨兵 |
| 单机内存不够 | Cluster |
| 写 QPS 单 Master 扛不住 | Cluster |
| 需要跨 key 事务很多 | 谨慎 Cluster,设计 hash tag 或避免跨 slot |
| 强一致账本 | 不适合只放 Redis |
商业场景:缓存集群选型
医疗资产平台:
- 字典、资产详情、字段元数据缓存量不大:主从 + 哨兵足够。
- 如果资产详情 key 数量巨大,单机内存接近上限:考虑 Cluster。
- 如果某个机构资产特别热:Cluster 不一定解决单 key 热点,需要本地缓存和拆 key。
- 如果要同一资产多个 key 一起操作:用 hash tag 或避免跨 slot 事务。
常见坑
| 坑 | 后果 | 正确理解 |
|---|---|---|
| 以为主从不丢数据 | 异步复制可能丢 | Redis 不做强一致账本 |
| 以为哨兵能扩容 | 哨兵只做高可用 | 容量扩展要 Cluster |
| 以为 Cluster 解决热 Key | 单 key 仍落一个 slot | 热 Key 需本地缓存、拆 key |
| 不处理 MOVED/ASK | 客户端访问失败或抖动 | 使用支持 Cluster 的客户端 |
| 滥用 hash tag | slot 热点 | 只给必须同 slot 的 key 使用 |
面试标准回答
Redis 主从复制用于数据副本和读扩展,第一次同步通常由 Master 生成 RDB 发给 Slave,同时把期间的写命令放入复制缓冲区,Slave 加载 RDB 后再重放增量命令。断线后如果 Master 的复制积压缓冲区还保留缺失命令,可以增量复制,否则要全量同步。哨兵是在主从基础上做高可用,先主观下线,再多个 Sentinel 达成客观下线,选举 Sentinel Leader,选择合适 Slave 晋升为新 Master,并让其他 Slave 改为复制新 Master。Cluster 通过 16384 个 hash slot 分片,key 根据 CRC16 计算 slot,slot 归属某个 Master。MOVED 表示 slot 已稳定迁移,客户端要更新映射;ASK 表示迁移中临时重定向。主从、哨兵、Cluster 都不能保证 Redis 成为强一致账本,异步复制和故障切换仍可能丢数据。