--- tags: [Redis, 缓存, 高可用, 主从, Sentinel] create time: 2026-05-15 18:13 --- # Redis 主从复制与 Sentinel ## 概述 在生产环境中,单节点 Redis 存在两大问题:**写入能力有上限**、**宕机即停服**。本节介绍两个构建基础高可用的核心机制—— | 机制 | 解决什么问题 | 一句话说明 | |------|------------|----------| | **主从复制(Replication)** | 数据冗余 + 读扩展 | Master 处理写操作,Replica 自动同步数据并承担读请求 | | **Sentinel(哨兵)** | 人工恢复慢 | 自动检测 Master 故障,提升最佳 Replica 为新 Master | > [!SUMMARY] 知识定位 > - ✅ 本方案适合 **读多写少** 的场景(8:2 ~ 9:1) > - ⚠️ 写入仍然集中在单点,若需水平写入 → [[hhs/Redis/07-集群方案]] > - 🔧 这是学习 Redis Cluster 的前置知识 ```mermaid flowchart TD Master["Master
写操作入口"] R1["Replica-1"] R2["Replica-2"] R3["Replica-3"] Master -->|"全量/增量同步"| R1 Master -->|"全量/增量同步"| R2 Master -->|"全量/增量同步"| R3 ClientRW["写请求"] --> Master ClientR["读请求"] --> R1 ClientR --> R2 ClientR --> R3 ``` ## 一、主从复制原理 ### 异步 vs 半同步 > [!QUESTION] 为什么 Redis 选择异步复制? > 如果等 Replica 确认后 Master 才返回客户端,那网络延迟 = 写入延迟。对于追求低延迟的场景(微秒级),这是不可接受的。 Redis 默认**异步复制**——Master 写完即返回客户端,不等待 Replica 确认。这保证了极致低延迟,代价是极端情况下可能丢失数据: ```text 场景: 写 A=1 → Master 返回 OK → Master 宕机(还没传到 Replica)→ 重启后 A 不存在 ``` 若业务需要 **至少一份副本落盘** 的强一致性保证,可配合 Lua + `wait` 命令实现弱半同步: ```go // Go: 确保至少 N 个副本成功写入 numReplicas := 1 // 至少 1 个 replica 收到 timeout := 100 // 超时 100ms result := client.Do(ctx, "WAIT", numReplicas, timeout).Int() // result == 1 → 至少有 1 个副本已同步 ``` > [!TIP] WAIT 的代价 > 阻塞当前线程直到满足条件或超时。高频写场景慎用,建议仅在关键事务中使用。 ### 全量同步 vs 增量同步 > [!NOTE] PSync:一次连接,多次复用 > Redis 2.8+ 引入 PSYNC 协议替代旧版 SYNC,核心优势:**断线重连后可增量同步**。 ```mermaid sequenceDiagram participant R as Replica participant M as Master Note over R,M: ── 阶段 1: 全量同步(首次或断线过长)── R->>M: PSYNC ? -1 (首次连接) M->>M: BGSAVE → dump.rdb.tmp M-->>R: +CONTENTS dump.rdb R->>R: 重写本地数据集 loop 同步期间的新命令 M->>M: 追加到 repl-backlog-buffer end M-->>R: +CONTENTS buf (缓冲命令流) R->>R: 重放缓冲命令 Note over R,M: ── 阶段 2: 增量同步(后续心跳)── loop 通常 1 秒一次 R->>M: PSync M->>R: 发送 offset 之后的命令 end ``` #### 什么情况下触发全量同步? | 触发条件 | 说明 | |---------|------| | 首次连接 | Replica 为空,必须下载完整 RDB | | Master 重启 | Run ID 改变,无法增量恢复 | | Replica 手动 SLAVEOF | 主动请求全量同步 | | Backlog 过小 | 断线时间超出 repl-backlog-size 缓存容量 | | config-resetstat 执行 | 元数据重置导致无法增量 | ### 复制背调(Replication Backlog) 每个 Master 内部维护一个固定大小的环形缓冲区(默认 1MB): ```mermaid flowchart LR WriteHead["写入指针"] -->|"新命令流入"| Buffer["repl-backlog-buffer
环形缓冲区(默认1MB)"] ReadHead["读取指针"] -->|"命令回放给 Replica"--> Buffer Buffer -->|"溢出"`覆盖最旧数据`" ``` ```conf # 相关配置 repl-backlog-size 256mb # 缓冲区大小,增大允许更长断线恢复 repl-backlog-ttl 3600 # 无人订阅时多久自动释放(秒) ``` **经验估算公式**:假设业务 QPS = 10,000,每条命令平均 50 字节,则每秒消耗 ~500KB 缓冲区。 - backlog = 1MB → 约支持 **2 秒** 断线恢复 - backlog = 256MB → 约支持 **8 分钟** 断线恢复 ### 大键分裂问题(Big Key Split) 当某个 key 体积很大时(如几 MB 的 Hash/Set),一次 `SET` 会阻塞整个 Redis 进程数毫秒甚至更久。对 Replica 来说,这个命令在网络上传输也会成为瓶颈。 > [!SUMMARY] 如何发现大 Key? > ```bash > redis-cli --bigkeys # 在线扫描(谨慎!高 QPS 环境有性能影响) > scan 0 MATCH * COUNT 100000 # 逐页扫描,用 type/hashlist/set 分别评估 > ``` 解决方案: - 写入侧:**分拆大 key** 为多个小 key(如按用户 ID 哈希分散) - 同步侧:增大 `repl-diskless-sync-delay` 让多个 Replica 同时接收,避免重复传输 ## 二、级联复制(拓扑优化) 当 Replica 数量增多时,每个都连 Master 会造成带宽压力: ```mermaid flowchart LR Master["Master"] R1["Replica-1
(也接受从连接)"] R2["Replica-2"] R3["Replica-3"] R4["Replica-4"] Master --> R1 Master --> R2 R1 --> R3 R1 --> R4 ``` > [!TIP] 最佳实践 > 只有 1~2 个 Replica 直接连 Master,其余通过级联获取数据。降低 Master 的网络和 CPU 负担。 ## 三、只读副本注意事项 ```conf replica-read-only yes # 默认值 ``` > [!WARNING] 禁止在 Replica 上写操作 > 即使关闭 `read-only`,Replica 上的写入在主从重新同步时会被清掉。而且 `DEL` 一个不存在的 key 也会触发额外命令发送给 Master。 ### Replica 的其他关键配置 ```conf # --- 副本如何发现 Master --- replica-announce-ip 192.168.1.20 # 对外宣告的 IP(Docker/NAT 环境下必设) replica-announce-port 6379 # 对外宣告的端口 # --- Replica 是否可被查询 --- replica-lazy-flush no # FULLRESYNC 后清空旧数据策略:no=立即 flush replica-serve-stale-data yes # Master 不可达时是否继续服务请求(默认是) replica-read-only yes # 只读保护 # --- 副本同步相关 --- repl-diskless-sync no # 无盘复制开关 repl-diskless-sync-delay 5 # 等待其他副本同时连接的延迟(秒) repl-timeout 60 # 同步超时阈值 ``` ## 四、核心参数调优参考 > [!NOTE] 根据业务特点选择合适的参数组合 ### 方案 A:极致性能优先 ```conf # 适用于读 QPS > 50k,对一致性要求不高 repl-backlog-size 256mb # 大容量 backlog 减少全量同步 repl-diskless-sync yes # 无盘复制加速 RDB 传输 replica-lazy-flush no # 确保同步前快速清理旧数据 ``` ### 方案 B:一致性优先 ```conf # 适用于金融、账务等场景 WAIT 1 100 # 写入时确认至少 1 个副本成功 repl-backlog-size 512mb # 更大缓冲区容纳更多增量命令 ``` ### 常用调试命令 ```bash # 查看当前复制状态 redis-cli INFO replication # 输出示例: # role:master ← 或 replica # connected_slaves:3 # master_repl_offset:12345678 # repl_backlog_active:1 # repl_backlog_size:268435456 ``` ## 五、Sentinel 哨兵机制 ### 架构组成 ```mermaid flowchart TD S1["Sentinel-1"] S2["Sentinel-2"] S3["Sentinel-3"] MasterNode["Master"] RepNode["Replica"] S1 <-->|心跳 PING/PONG| S2 S1 <-->|心跳 PING/PONG| S3 S2 <-->|心跳 PING/PONG| S3 S1 <--> MasterNode S2 <--> MasterNode S3 <--> MasterNode MasterNode <--> RepNode ``` > [!QUESTION] 为什么需要奇数个 Sentinel? > Sentinel 选举采用多数派原则(Quorum)。3 个节点中 2 个达成共识即可;5 个中需要 3 个。偶数增加 1 个 Sentinel 不会带来额外投票优势,反而浪费资源。 ### 核心功能 #### 1. 监控(Monitoring) ```bash # Sentinel 定期向 Master/Replica 发 PING # Master 必须响应:PING → PONG 或 INFO # Replica 同理 # 三种下线判断 1. SDOWN (Subjectively Down) — 某个 Sentinel 认为不可达 2. ODOWN (Objectively Down) — Quorum 数量的 Sentinel 都认为不可达 3. Failover in progress — 进入故障转移流程 ``` #### 2. 选主(Leader Election) ```mermaid sequenceDiagram participant S1 as Sentinel-1 participant S2 as Sentinel-2 participant S3 as Sentinel-3 S1->>S2: 提议自己为 leader (SEND ME YOUR VOTE) S2->>S1: OK (投票给 S1) S3->>S1: OK (投票给 S1) S1->>S2: 我当选 leader Note over S1,S3: ⚡ 任意 Sentinel 都可以主动发起选举 ``` ### 故障转移步骤 当 Master 被判定 ODOWN 后,Sentinel Leader 执行以下流程: ```mermaid flowchart TD A["1. 选出最佳 Replica"] --> B["评估标准:
复制偏移量 > 优先级 > RunID"] B --> C["SLAVEOF NO ONE
提升为新 Master"] C --> D["其他 Replica → SLAVEOF new-master"] D --> E["更新集群元数据"] E --> F["通知客户端新地址"] ``` **最佳 Replica 选择标准**(按优先级排序): 1. **复制偏移量最大** — 离最新数据的 Replica 优先 2. **`replica-priority` 最小** — 值越小越优先当选(0 = 不可当选) 3. **Run ID 字典序最小** — 作为最终 tie-breaker ### Sentinel 配置文件 ```conf sentinel monitor mymaster 192.168.1.10 6379 2 # ↑ name ↑ host port quorum(达成SDOWN共识的个数) sentinel auth-pass mymaster your-password # 多久无响应即标记 SDOWN(毫秒) sentinel down-after-milliseconds mymaster 30000 # 故障转移超时(毫秒),超过则失败重试 sentinel failover-timeout mymaster 180000 # 最多多少 Replica 同时与新 Master 同步 sentinel parallel-syncs mymaster 1 ``` > [!WARNING] Sentinel 配置陷阱 > - `down-after-milliseconds` 不要设太低(如 1s),网络抖动会导致误判;建议 **15~30 秒** > - `failover-timeout` 不宜太短,防止频繁重试加重系统负载 > - `parallel-syncs` 设为 1 可避免转移期间所有 Replica 同时不可用 > - **生产环境建议手动维护 Sentinel 配置**,避免自动感知的不确定性 > - Redis 6+ 启用 ACL 后,需配置 `sentinel user/myuser password/mypass` 而非 `auth-pass` ### Sentinel 下的客户端连接 Go 和 Python 生态都提供了原生支持——客户端内部自动发现 Master/Replica,故障时重新连接。 ```go // go-redis: 原生 Sentinel 模式 rdb, err := redis.NewFailoverClient(&redis.FailoverOptions{ MasterName: "mymaster", SentinelAddrs: []string{"sento1:26379", "sento2:26379", "sento3:26379"}, Password: "your-password", MaxRetries: 3, RetryDelay: 500 * time.Millisecond, }) // 读请求走 Replica,写请求走 Master _ = rdb.Get(ctx, "key") ``` ```python # redis-py Sentinel from redis.sentinel import Sentinel sentinel = Sentinel([('sento1', 26379), ('sento2', 26379), ('sento3', 26379)]) master = sentinel.master_for('mymaster', password='your-password') # 写 slave = sentinel.slave_for('mymaster', password='your-password') # 读 ``` ## 五、生产实践 checklist ### 部署前 - [ ] **Sentinel 节点数 ≥ 3**,奇数个,部署在不同可用区或机器上 - [ ] **Master + Replica 数量建议 1:2 ~ 1:3**,过多会影响同步延迟 - [ ] **`replica-priority` 设值**:不可作为 Master 的设为 `0` - [ ] **持久化策略一致**:Master 和 Replica 的 RDB/AOF 策略应相同 ### 监控项 ```bash # 关键指标(每秒采样) INFO replication # connected_slaves, master_repl_offset INFO memory # used_memory_human (同步期间会突增) INFO stats # instantaneous_ops_per_sec ``` ### 故障排查流程 ```mermaid flowchart TD A["发现 Master 异常"] --> B{"Sentinel 是否已 ODOWN?"} B -->|"是"| C["自动 Failover 中..."] B -->|"否"| D["检查网络 / CPU / 大 Key 阻塞"] C --> E["Failover 成功?"] E -->|"是"| F["客户端已自动切换 ✓"] E -->|"否"| G["手动干预:
SENTINEL FAILOVER mymaster"] D --> H["问题解决后
重新加入复制拓扑"] ``` ### 常见坑点汇总 | 坑 | 现象 | 解决 | |---|------|------| | Docker 环境下 IP 错乱 | Sentinel 记录了容器的内部 IP | 设置 `replica-announce-ip` | | 脑裂(Split Brain) | 网络分区后 Master 仍处理写请求 | 用 `min-replicas-to-write 1` 保护 | | OOM during BGSAVE | fork 子进程导致内存翻倍 | 关闭 THP,增大 swap,限制 maxmemory | > [!NOTE] 防脑裂配置 > ```conf > # Master 端配置:至少 N 个 Replica 在线才接受写入 > min-replicas-to-write 1 > # 对应的最大允许延迟(秒),超时视为掉线 > min-replicas-max-lag 10 > ``` ## 六、局限性与替代方案 | 问题 | Sentinel 无法解决 | |------|----------------| | 单 Master 写入瓶颈 | ❌ 只有一个 Master | | 数据量超限单机容量 | ❌ | | 跨机房容灾 | ❌(Sentinel 跨网段延迟太高) | > [!TIP] 需要水平扩展?看 Cluster 方案 → [[hhs/Redis/07-集群方案]] ## 关联笔记 - [[hhs/Redis/07-集群方案]] — Cluster 架构(水平扩展) - [[hhs/Redis/09-运维调优]] — 监控与告警配置 - [[hhs/Redis/04-AOF持久化]] — AOF 与 RDB 结合的高可用策略 - [[hhs/EXAM/Week05]] — Docker Compose 中的部署示例