一句话回答
Redis 通过 RDB 快照和 AOF 日志实现数据持久化,通过异步主从复制保存副本,使用 Sentinel 完成主从架构的监控与自动切换,或使用 Redis Cluster 同时实现分片和故障转移。它们通常只能降低数据丢失风险,不能自动提供跨节点强一致。
面试考察点
- 能否区分 RDB 和 AOF 的数据安全、恢复速度与运行开销。
- 是否理解主从复制通常是异步的,故障切换可能丢数据。
- 能否描述全量同步、增量同步和复制积压缓冲区。
- 是否知道 Sentinel 解决高可用但不解决数据分片。
- 能否说明 Cluster Slot、重定向和故障转移。
- 是否具备脑裂、磁盘故障和恢复演练意识。
RDB 快照
RDB 在某个时间点生成内存数据快照文件,适合备份、全量复制和快速恢复。
优点:
- 文件紧凑,适合备份和跨机房传输。
- 重启恢复通常比重放大量 AOF 快。
- 主进程主要负责 Fork,具体写文件由子进程完成。
不足:
- 两次快照之间的数据可能在故障时丢失。
- Fork 大进程会带来页表复制和延迟抖动。
- 快照期间写入导致 Copy-on-Write,可能增加内存峰值。
- 磁盘慢或内存大时生成时间较长。
Fork 和 Copy-on-Write
生成 RDB 时,子进程读取 Fork 时刻的内存视图。主进程继续处理写入;被修改的内存页复制后由主进程使用,子进程仍看到旧页。
因此执行 BGSAVE 时要关注:
- 实例内存大小。
- 系统剩余内存。
- 写入速率。
- Transparent Huge Pages。
- Fork 耗时和磁盘吞吐。
高写入期间 COW 可能让 RSS 明显增长,容器 Limit 过紧会导致 OOMKilled。
AOF 日志
AOF 记录会改变数据的写命令,重启时重放恢复。
常见刷盘策略:
- Always:每条命令同步磁盘,安全性高但吞吐和延迟成本大。
- Everysec:通常每秒刷盘一次,性能与数据安全折中,故障时可能丢约一秒数据。
- No:由操作系统决定刷盘,性能较好但丢失窗口更大。
生产常选择 Everysec,但要根据业务可接受的数据损失目标决定。
AOF Rewrite
AOF 持续追加会不断变大。Rewrite 根据当前内存状态生成等价但更紧凑的命令,例如一个 Key 被修改一万次,重写后只保留恢复当前值所需的命令。
Rewrite 同样涉及 Fork、COW、CPU 和磁盘 IO。不能只关注 AOF 文件大小,还要监控重写持续时间、失败次数和磁盘余量。
RDB 和 AOF 怎么选
| 维度 | RDB | AOF |
|---|---|---|
| 数据丢失窗口 | 快照间隔 | 取决于刷盘策略 |
| 文件大小 | 通常较小 | 通常较大 |
| 恢复速度 | 通常较快 | 需要重放日志 |
| 运行开销 | 周期性 Fork 与写文件 | 持续追加与定期重写 |
| 备份 | 适合 | 也可使用但管理更复杂 |
重要数据可以同时开启 RDB 和 AOF。具体 Redis 版本的加载优先级和混合持久化能力需要结合官方文档和配置确认。
混合持久化
混合模式在 AOF 重写文件前部保存 RDB 格式快照,后部追加重写期间的命令,兼顾恢复速度和 AOF 的较小数据丢失窗口。
启用前要确认 Redis 版本、运维工具和备份流程都支持该格式。
持久化不等于备份
误删除和错误写入也会进入 AOF 和副本。持久化用于进程重启恢复,高可用用于实例故障切换,历史恢复仍需要独立备份、保留周期和恢复演练。
主从复制流程
Replica 连接 Master
↓
身份验证与复制握手
↓
能增量同步?
├── 是:发送积压缓冲区中的命令
└── 否:生成 RDB 全量同步
↓
Replica 加载快照
↓
持续接收新写命令
主从复制通常异步,Master 执行成功后不会等待所有 Replica 应用。因此刚写入的数据可能暂时无法从 Replica 读取,Master 故障时尚未同步的数据也可能丢失。
全量同步
Replica 首次加入或断线太久时进行全量同步。Master 生成快照并发送,同时把期间的新写命令保存在缓冲区;Replica 加载快照后再应用增量命令。
大实例全量同步会消耗 Fork、磁盘、网络和 Replica 加载时间。多个 Replica 同时全量同步可能形成复制风暴。
增量同步
Master 维护复制 ID、Offset 和复制积压缓冲区。Replica 短暂断线重连后,如果缺失数据仍在缓冲区,可以只补发断线期间命令。
缓冲区过小会让稍长网络抖动退化为全量同步。大小应结合写入速率和预期最长断线时间配置。
复制延迟
常见原因:
- 网络延迟或带宽不足。
- Replica CPU、磁盘或内存压力。
- Master 大量写入。
- Big Key 传输和处理。
- Lua 或慢命令阻塞事件循环。
- Replica 执行持久化和全量同步。
读写分离时必须接受短暂旧数据。写后立刻读取关键状态应读 Master,或使用业务版本校验。
Sentinel 解决什么问题
Sentinel 监控 Master 和 Replica,在 Master 故障时选择 Replica 提升为新 Master,并通知其他 Replica 和客户端切换。
它提供:
- 监控与故障检测。
- 主观下线和客观下线判断。
- Leader Sentinel 选举。
- Replica 选主和故障转移。
- 客户端获取当前 Master 地址。
Sentinel 不负责数据分片,数据集仍完整保存在每个 Master/Replica 节点。
主观下线与客观下线
单个 Sentinel 认为节点不可达是主观下线。达到配置数量的 Sentinel 都认为 Master 不可达,才形成客观下线并尝试故障转移。
这样可以减少单个 Sentinel 网络异常造成误切换,但仍需合理部署到不同故障域,并处理网络分区。
Sentinel 选新 Master 的考虑
通常综合 Replica 优先级、复制状态、数据 Offset 和稳定性选择。数据越接近原 Master 的 Replica 越适合提升。
故障切换期间客户端会经历短暂失败,需要使用支持 Sentinel 的客户端并配置重连、有限重试和幂等。
脑裂和数据丢失
网络分区时旧 Master 可能仍接收写入,而 Sentinel 在另一侧提升新 Master。网络恢复后旧 Master 降为 Replica,它在分区期间接收的写入可能丢失。
可通过限制 Master 在可用 Replica 不足或复制延迟过大时拒绝写入,降低脑裂损失。但这是用可用性换数据安全,仍无法提供严格共识一致性。
Redis Cluster
Cluster 将 16384 个 Slot 分配给多个 Master,每个 Master 管理部分数据,并可配置 Replica。
Key → CRC16 → Slot 0..16383 → 对应 Master
它解决:
- 数据容量超过单机内存。
- 写入和读取压力水平分散。
- 单 Master 故障时由 Replica 提升。
MOVED 和 ASK
客户端请求到错误节点时:
- MOVED 表示 Slot 已稳定属于另一个节点,客户端应更新拓扑缓存。
- ASK 常见于 Slot 迁移过程中,表示本次请求临时去目标节点,但不立即永久修改归属。
生产应使用支持 Cluster 拓扑感知的客户端,不要自己手写重定向逻辑。
Cluster 的限制
- 多 Key 操作通常要求 Key 在同一 Slot。
- 故障切换仍可能丢失未同步写入。
- Slot 迁移时 Big Key 会造成抖动。
- 节点扩缩容涉及数据迁移和带宽。
- 热 Key 可能让单 Slot、单节点过载。
- Cluster Bus 和节点数量增加运维复杂度。
Sentinel 和 Cluster 怎么选
- 数据量能放入单机、主要需要自动主从切换:Sentinel。
- 数据量或吞吐需要水平分片:Cluster。
- 强一致事务、跨 Key 复杂关系:重新评估 Redis 是否适合做事实源。
云厂商代理版 Redis 可能隐藏 Sentinel 或 Cluster 细节,但数据丢失窗口、分片限制和故障语义仍需向厂商确认。
故障切换期间客户端设计
- 使用连接池和拓扑感知客户端。
- 设置连接、命令和重试超时。
- 只对幂等操作进行有限重试。
- 写失败不能无限重试并堵塞业务线程。
- 关键写入要有数据库或消息日志作为事实源。
- 熔断和降级避免重连风暴。
数据恢复流程
- 隔离故障实例,避免继续接受写入。
- 保存 RDB、AOF 和日志副本。
- 在隔离环境验证文件完整性。
- 启动临时实例加载数据。
- 对比业务数据库或事件日志进行校验。
- 根据恢复点补偿缺失数据。
- 灰度切换流量并持续观察。
不要直接拿未经验证的持久化文件覆盖线上当前数据。
监控指标
used_memory、RSS 和内存碎片率。- RDB/AOF 最近成功时间和失败次数。
- AOF 重写状态和文件大小。
- 主从复制 Offset 和延迟。
- Replica 连接状态与全量同步次数。
- Sentinel 切换事件。
- Cluster Slot 覆盖和失败节点。
- 磁盘容量、Fork 耗时和 COW 内存。
常见误区
- 开启 AOF 就绝对不丢数据。
- 有 Replica 就等于完成备份。
- Sentinel 能解决数据分片。
- Cluster 能提供强一致。
- Redis 只使用内存,磁盘性能不重要。
- Fork 不会影响线上延迟。
- 故障切换后客户端会自动无感恢复。
核心考点清单
- RDB 适合快照备份和快速恢复,AOF 提供更小的数据丢失窗口。
- Fork 和 COW 会带来延迟和内存峰值。
- 主从复制通常异步,故障切换可能丢失未同步数据。
- Sentinel 提供单主多从的自动故障转移,不负责分片。
- Cluster 使用 16384 个 Slot 实现数据分片和节点扩展。
- 高可用、持久化和备份解决不同问题,三者都需要。
高频追问与参考回答
追问 1:RDB 和 AOF 应该怎么选?
缓存可只使用 RDB 或接受重建;重要数据通常同时使用 RDB 与 AOF,并选择 Everysec 等合适刷盘策略。但 Redis 仍不应替代关键业务事实数据库。
追问 2:AOF Everysec 会丢多少数据?
通常可能丢失最近约一秒尚未同步磁盘的数据,但具体还受操作系统、磁盘和 Redis 实现影响。机器或磁盘故障场景不能做绝对承诺。
追问 3:为什么主从切换会丢数据?
复制异步,Master 已确认但尚未发送或 Replica 尚未应用的写入,在原 Master 故障后不会存在于新 Master。
追问 4:Sentinel 需要几个节点?
通常至少三个并部署在不同故障域,以形成多数判断。还要根据 Quorum 和故障转移授权配置,不能只看进程数量。
追问 5:Redis Cluster 为什么是 16384 个 Slot?
Slot 是 Key 到节点之间的逻辑分片层,便于迁移和拓扑管理。固定数量在集群消息开销和分片粒度间折中,客户端无需为每个 Key 保存节点映射。
追问 6:如何减少脑裂造成的数据丢失?
限制 Master 在可用 Replica 数不足或复制延迟过大时拒绝写入,将节点分布在合理故障域,并让关键业务拥有外部事实源和补偿。这会牺牲部分可用性。
追问 7:持久化文件损坏怎么办?
先复制原文件,在隔离环境使用官方检查和修复工具评估,再结合历史备份和业务事实源恢复。不要直接在唯一线上副本上操作。
机制全景图
「Redis 持久化和高可用是怎么实现的?」的实现链路如下,节点可与后面的源码和运行证据逐一对应。
flowchart LR
A["内存写入完成"]
A --> B["AOF 或 RDB 持久化"]
B --> C["主从复制传播"]
C --> D["Sentinel/Cluster 检测故障"]
D --> E["选主恢复并核对数据"]
源码与实现定位
| 入口 | 阅读重点 |
|---|---|
| src/rdb.c/aof.c | 快照、AOF 与重写 |
| INFO persistence/replication | fork、fsync、复制偏移 |
源码或系统表应按上表顺序追踪:先确认入口实际走到哪条路径,再用运行时数据验证,而不是仅凭类名或配置推测。
参数配置与可复现实验
CONFIG SET appendonly yes
CONFIG SET appendfsync everysec
CONFIG SET repl-backlog-size 256mb
分别 kill -9 主库、断开副本超出 backlog、在高写入时 BGSAVE,测 RPO、RTO 与 COW 内存。
验证步骤与预期结果
1. 固定输入和基线
先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「fork COW」为主基线,记录值应满足「<容器余量」;同时保存 aof_delayed_fsync、fork 耗时与 COW,使后续变化能够回到同一时间轴比较。
2. 从实现入口确认路径
在「src/rdb.c/aof.c」确认请求确实进入「快照、AOF 与重写」对应的实现,再沿「INFO persistence/replication」观察「fork、fsync、复制偏移」。如果入口路径都未命中,就不应继续调整下游参数,而应先检查调用条件、版本或路由是否与假设一致。
3. 注入本文特有的失败模式
优先复现「fork 时写时复制导致内存翻倍」,并把单一变量逐级放大,直到「fork COW」越过「RSS逼近limit」。随后再分别验证「复制积压不足频繁全量同步」和「把异步副本当成强一致备份」,三类故障分开执行,避免多个变量同时变化而无法归因。
4. 执行止损和根因修复
第一轮只应用「为 fork 预留内存」,确认它能控制影响范围;第二轮应用「backlog 覆盖最长断线窗口」,验证核心链路恢复;最后落实「明确 Redis 是否可从事实源重建」,消除同类问题再次出现的条件。每一步都保留变更前后数据,不用“感觉变快了”替代测量。
5. 通过退出条件
实验只有同时满足三项才算通过:「fork COW」回到「<容器余量」、「复制偏移差」回到「故障目标内」、「aof delayed fsync」回到「0 或偶发」,并且业务结果差异为零。若性能恢复但结果不一致,仍应视为失败;若指标恢复后很快再次越线,则说明只完成了临时止损,没有消除根因。
量化基线
| 指标 | 样例基线/口径 | 风险线 | 结论 |
|---|---|---|---|
| fork COW | <容器余量 | RSS逼近limit | 快照风险 |
| 复制偏移差 | 故障目标内 | 超 backlog | 全量同步 |
| aof delayed fsync | 0 或偶发 | 持续增长 | 磁盘抖动 |
这些数值是实验口径或示例告警线,不是可复制到所有系统的固定答案;上线阈值应由本系统稳态、峰值和故障演练共同确定。
事故复盘:故障切换后少量订单资格丢失
主库返回成功后尚未把写传播到副本就宕机,新主缺少该 Key。异步复制不能承诺零丢失;资格状态若不可丢,应落可靠数据库/日志或在写入时等待足够副本并接受延迟。
| 失败模式 | 首要证据 | 第一处置动作 |
|---|---|---|
| fork 时写时复制导致内存翻倍 | aof_delayed_fsync | 为 fork 预留内存 |
| 复制积压不足频繁全量同步 | fork 耗时与 COW | backlog 覆盖最长断线窗口 |
| 把异步副本当成强一致备份 | master_repl_offset 差距 | 明确 Redis 是否可从事实源重建 |
发布与回滚检查点
- 发布前:确认「src/rdb.c/aof.c」对应实现和上述配置在目标版本仍然有效,并保存「fork COW」基线。
- 灰度中:同时观察 aof_delayed_fsync、fork 耗时与 COW、master_repl_offset 差距;任一指标越过表中风险线,就停止继续扩量。
- 回滚时:先执行「为 fork 预留内存」控制影响,再回退代码或参数;涉及持久状态时必须额外核对结果差异。
- 发布后:至少覆盖一个完整峰值周期,确认「fork 时写时复制导致内存翻倍」没有再次出现,才关闭变更观察窗口。
设计边界与工程取舍
高可用保证服务尽快恢复,不自动保证所有已确认写都存在;必须按数据价值选择 Redis 是事实源还是可重建派生数据。