Skip to content

这是我学习 Redis 高可用时整理的笔记。持久化解决"重启不丢数据"(见《Redis 深入:持久化与过期淘汰》),本文解决"一台挂了怎么办"——主从复制、哨兵故障转移、集群分片三层演进。

一、解决什么问题

单机 Redis 有三个问题:

问题后果
单点故障机器一挂,缓存/锁全没,应用直接雪崩
读压力一台机器扛不住海量读
容量上限单机内存装不下全部数据

解决思路是演进式的:主从复制(扛读)→ 哨兵(自动切换)→ 集群(扛数据量)。

二、主从复制:一主多从,读写分离

结构:一个 Master(写)+ 多个 Replica(读),数据实时同步。

Master(写) ──同步──► Replica 1(读)
                  └──► Replica 2(读)

同步机制(全量 + 增量)

  1. 全量同步:Replica 首次连接,Master BGSAVE 生成 RDB 传给 Replica 加载(期间新命令进缓冲区)
  2. 增量同步:之后 Master 把写命令通过复制积压缓冲区持续推给 Replica(类似 AOF 重放)

作用:读压力分摊到多个 Replica;Master 挂了复制关系还在,但写不了——需要手动切换(replicaof no one 提升从库)。这就是哨兵要解决的问题。

三、哨兵 Sentinel:自动故障转移

结构:在主从上加一组 Sentinel 进程(至少 3 个,部署在不同机器),专门监控 Redis 健康:

        Sentinel 1 / 2 / 3(互相投票,防误判)
              │ 监控
   Master ◄──┴──► Replica 1 / Replica 2

故障转移流程

  1. 主观下线:某个 Sentinel 发现 Master 心跳超时(down-after-milliseconds
  2. 客观下线:该 Sentinel 发起投票,多数 Sentinel(≥2/3)同意才确认 Master 真挂了(防止网络抖动误杀)
  3. 选新主:从 Replicas 里选一个(优先级/复制进度/runid),执行 replicaof no one 提升
  4. 通知:其他 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 实测)

① 主从复制(一主一从):

bash
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):

bash
# 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 后 replica get 到 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 需可写挂载(哨兵会把状态写回配置文件)。