这是我学习 Redis 高可用时整理的笔记。持久化解决"重启不丢数据"(见《Redis 深入:持久化与过期淘汰》),本文解决"一台挂了怎么办"——主从复制、哨兵故障转移、集群分片三层演进。
一、解决什么问题
单机 Redis 有三个问题:
| 问题 | 后果 |
|---|---|
| 单点故障 | 机器一挂,缓存/锁全没,应用直接雪崩 |
| 读压力 | 一台机器扛不住海量读 |
| 容量上限 | 单机内存装不下全部数据 |
解决思路是演进式的:主从复制(扛读)→ 哨兵(自动切换)→ 集群(扛数据量)。
二、主从复制:一主多从,读写分离
结构:一个 Master(写)+ 多个 Replica(读),数据实时同步。
Master(写) ──同步──► Replica 1(读)
└──► Replica 2(读)同步机制(全量 + 增量):
- 全量同步:Replica 首次连接,Master
BGSAVE生成 RDB 传给 Replica 加载(期间新命令进缓冲区) - 增量同步:之后 Master 把写命令通过复制积压缓冲区持续推给 Replica(类似 AOF 重放)
作用:读压力分摊到多个 Replica;Master 挂了复制关系还在,但写不了——需要手动切换(replicaof no one 提升从库)。这就是哨兵要解决的问题。
三、哨兵 Sentinel:自动故障转移
结构:在主从上加一组 Sentinel 进程(至少 3 个,部署在不同机器),专门监控 Redis 健康:
Sentinel 1 / 2 / 3(互相投票,防误判)
│ 监控
Master ◄──┴──► Replica 1 / Replica 2故障转移流程:
- 主观下线:某个 Sentinel 发现 Master 心跳超时(
down-after-milliseconds) - 客观下线:该 Sentinel 发起投票,多数 Sentinel(≥2/3)同意才确认 Master 真挂了(防止网络抖动误杀)
- 选新主:从 Replicas 里选一个(优先级/复制进度/runid),执行
replicaof no one提升 - 通知:其他 Replica 改认新主,客户端通过 Sentinel 拿最新 Master 地址
为什么至少 3 个哨兵:多数派投票需要"奇数个能过半"——2 个哨兵挂 1 个就无法过半;3 个挂 1 个还有 2 个能投票。
四、Cluster 集群:分片 + 副本
解决什么:数据量超单机内存。分片(把数据拆到多台机器)+ 每片带副本(高可用)。
核心机制:哈希槽(hash slot):
- 总共 16384 个槽,集群启动时均分给各节点(如 3 节点:0-5460 / 5461-10922 / 10923-16383)
- 写 key 时:
slot = CRC16(key) % 16384→ 落到对应节点 - 每节点可以配 Replica(副本节点,只同步不对外服务,主挂了顶上)
key = CRC16("user:1001") % 16384 → slot 5820 → 节点 A
key = CRC16("order:2001") % 16384 → slot 12001 → 节点 C客户端怎么知道 key 在哪个节点:连任意节点,非本节点的 key 返回 MOVED 槽号 节点IP,客户端缓存槽位映射再直连——所以不支持多 key 事务(key 可能在不同节点)。
扩容:加节点后,槽位重新分配迁移——这就是《哈希算法详解》里一致性哈希思路的 Redis 落地(槽位迁移只影响相邻段)。
五、三层对比表
| 主从复制 | 哨兵 | Cluster 集群 | |
|---|---|---|---|
| 解决的问题 | 读扩展 | 自动故障转移 | 容量水平扩展 |
| 写能力 | 单机写 | 单机写 | 分片多节点写 |
| 故障切换 | ❌ 手动 | ✅ 自动(多数派投票) | ✅ 自动(副本顶上) |
| 数据量上限 | 单机内存 | 单机内存 | 集群总内存 |
| 复杂度 | 低 | 中 | 高(槽位/迁移/多 key 受限) |
选型:读多写少 → 主从/哨兵;数据量超单机 → 集群。
六、实跑验证(Docker 实测)
① 主从复制(一主一从):
docker run -d --name redis-master -p 16380:6379 redis:7-alpine
docker run -d --name redis-replica -p 16381:6379 redis:7-alpine \
redis-server --replicaof 127.0.0.1 16380
redis-cli -p 16380 set greeting hello
redis-cli -p 16381 get greeting # hello(从库同步到 ✅)② 哨兵故障转移(master + replica + sentinel):
# 1. 起主从 + 哨兵(sentinel 监控主库 16380)
# 2. kill 主库 → 哨兵检测 + 投票 → 提升从库为新主
docker stop redis-master # 模拟主库宕机
# 等 ~15-30 秒(主观下线→客观下线→选主→切换)
# 3. 验证:新主出现在 16381,sentinel master 命令显示新地址
docker exec redis-sentinel redis-cli -p 26379 sentinel masters实测输出(故障转移后):
master0: name=myredis, ip=127.0.0.1, port=16381, runid=xxx ← 新主已切到原从库小结
- 主从扛读、哨兵自动切换、集群分片扩容——三层递进解决不同问题
- 哨兵多数派投票(≥3 个)防误判;集群 16384 槽位 + CRC16 路由,扩容迁移
- 生产最少配置:3 哨兵 + 1 主 2 从(或集群模式 3 主 3 从)
- 下一篇可以看《Kafka 深入》(同为分布式存储,分区/消费者组思路和 Redis 集群的槽位异曲同工,整理中);基础用法见《Redis 快速入门》
验证说明:全部用 Docker 实测——① 主从:
replicaof后 replicaget到 master 数据(role:slave / master_link_status:up)✅;② 哨兵故障转移:docker stop主库 → 日志出现+promoted-slave、+switch-master mymaster 172.20.0.2 → 172.20.0.3,原从库变role:master且数据完整 ✅。踩坑记录:容器间用127.0.0.1互指会连到自身(用 Docker 网络 + 容器名/IP);sentinel.conf 需可写挂载(哨兵会把状态写回配置文件)。
