Skip to content
Charles Shao
Go back

Redis 深挖(二)· 持久化与高可用

views

Redis 把数据放在内存里换来了速度,但也带来两个绕不开的问题:进程重启/宕机后数据还在吗(持久化)单个节点挂了服务还在吗(高可用)。这两个问题的答案,决定了你敢把多少业务押在 Redis 上——是当「挂了回源重建就行的纯缓存」,还是当「不能丢、不能停的在线数据源」。

本篇把这两条线讲透:先是 RDB / AOF / 混合三种持久化,再是主从复制 → Sentinel → Cluster 的高可用三级跳。

Redis 深挖三篇:本篇是第二篇「持久化与高可用」,另外两篇是 数据结构与内存模型线程模型与性能,都是 缓存系列开篇 的下钻。本篇的容灾/多活视角与 冗余与容灾 一脉相承。

TL;DR

Table of contents

Open Table of contents

1. 先分清:Redis 是「缓存」还是「数据源」

持久化要做到什么程度,取决于你怎么定位 Redis:

这个定位决定了后面所有取舍的松紧。

2. RDB:快照

RDB 是某一时刻内存的全量快照,序列化成紧凑二进制文件 dump.rdb

两条持久化路线对比:RDB 快照通过 BGSAVE fork 子进程 + 写时复制 COW 遍历内存写 dump.rdb(文件小恢复快,但丢数据窗口大、写多时内存翻倍抖动);AOF 命令追加把写命令追加到 aof_buf 再按 appendfsync 策略刷盘到 appendonly.aof(可 bgrewriteaof 重写);混合持久化则 RDB 头+AOF 尾兼顾两者

RDB 靠 fork+COW 做快照、文件小恢复快但丢数据窗口大;AOF 追加写命令、按 appendfsync 三档取舍安全与性能;混合持久化取两者之长。

代价

优点:文件紧凑、加载恢复最快,适合做定期备份、灾备传输、主从全量同步的载体。

3. AOF:命令追加

AOF(Append Only File)记录每一条写命令,重启时重放这些命令重建数据。

写入路径:写命令 → 追加到 aof_buf 内存缓冲 → 按 appendfsync 策略刷盘到 appendonly.aof

appendfsync行为丢数据性能
always每条写命令都 fsync几乎不丢最慢
everysec(默认)每秒 fsync 一次最多丢 ~1s均衡
no交给 OS 决定何时刷丢较多最快

AOF 重写(bgrewriteaof:AOF 会越追越大(对同一 key 反复写会留下一堆历史命令)。重写会 fork 子进程,用「当前内存状态」生成一份等价但更精简的 AOF(比如 100 次 INCR 压成一条 SET),替换旧文件。重写期间的新写命令先进缓冲区,重写完再追加,保证不丢。

优点:丢数据窗口小、可读性好。缺点:文件比 RDB 大、恢复(重放命令)比加载 RDB 慢、fork 同样有开销。

4. 混合持久化

RDB 恢复快但丢得多,AOF 丢得少但恢复慢——混合持久化(aof-use-rdb-preamble,4.0+ 默认开) 取两者之长:

AOF 重写时,先以 RDB 二进制格式写一个「全量头」,再把重写期间的增量写命令以 AOF 文本格式追加成「尾」。 重启加载时,先快速吃掉 RDB 头恢复大部分数据,再重放少量 AOF 尾补齐最新增量。

于是既有 RDB 的体积小、恢复快,又有 AOF 的丢数据窗口小。生产上常见配置:开 AOF(everysec)+ 开混合持久化 + 保留定期 RDB 备份;重启优先加载 AOF(数据更全)。

5. RDB vs AOF 怎么选

维度RDBAOF
丢数据窗口大(两次快照间)小(≤1s,everysec
文件体积大(重写后可控)
恢复速度慢(重放命令)
对写入的影响fork + COW 抖动每秒/每条 fsync
适合备份、灾备、能容忍丢一段不能丢、要点对点最新

结论:纯缓存可只留 RDB 甚至关持久化;当数据源用 AOF everysec + 混合持久化 + 定期 RDB 备份。要极致不丢用 always,但要接受吞吐下降。持久化不等于高可用——磁盘上数据还在,但进程挂了业务照样不可用,所以还要往下看主从与 Sentinel/Cluster。

6. 主从复制

高可用的第一步是别把鸡蛋放一个篮子:一主多从,主写从读,主挂了从能顶上。

关键一致性事实:Redis 复制是异步的。 主库把写返回客户端后才异步发给从库。所以主库突然宕机,那些「已回客户端但还没到从库」的写会在故障切换后丢失WAIT numreplicas timeout 能让写等待若干从库确认,但只是尽力,不是强一致。

7. Sentinel:自动故障转移

主从解决了「有备份」,但主挂了谁来切、怎么切、客户端怎么知道新主?这就是 Sentinel(哨兵)。

Sentinel 故障转移时序:正常态下 Sentinel 集群每秒 PING 监控 master/replica/彼此;master 宕机 PING 超时 → 超过 down-after-ms 主观下线 sdown → Sentinel 互询达到 quorum 票数客观下线 odown → raft-like 选出 leader → 选复制偏移最大的从 REPLICAOF NO ONE 升为新主 → 令其余从改指向新主 → 发布 +switch-master 通知客户端重连

Sentinel 监控主从与彼此;主观下线(单个 Sentinel 判定)达 quorum 变客观下线,选举 leader 执行故障转移,最后通过订阅通知客户端新主地址。

流程拆解:

  1. 监控:每个 Sentinel 每秒 PING 主/从/其他 Sentinel。
  2. 主观下线 sdown:某个 Sentinel 发现主库 PING 超时超过 down-after-milliseconds,标记它「我觉得它挂了」。
  3. 客观下线 odown:Sentinel 之间互相询问(is-master-down-by-addr),当认为主库挂了的 Sentinel 数达到 quorum,判定「大家都觉得它挂了」。
  4. 选举:Sentinel 用类 Raft 的方式选出一个 leader 来主导本次故障转移。
  5. 提升新主:leader 从从库里挑**复制偏移量最大(数据最全)**的那个,REPLICAOF NO ONE 让它变主,其余从库改指向新主。
  6. 通知客户端:Sentinel 发布 +switch-master 事件,订阅了的客户端拿到新主地址并重连。

Sentinel 数量要 ≥3 且为奇数,且分布在不同故障域——否则脑裂或选不出 leader。故障转移需要时间(探测超时 + 选举 + 切换),这段窗口客户端可能读写失败,要靠客户端重试 + 本地缓存兜底(见 §10)。

8. Redis Cluster:分片 + 高可用

主从+Sentinel 解决了 HA,但所有数据仍在一个主库里——单机内存/写吞吐是天花板。Cluster 通过分片横向扩展,同时自带故障转移。

Redis Cluster 分片与路由:数据分到 16384 个槽,Master A/B/C 各负责一段槽区间并各带一个 Replica 异步复制;客户端缓存槽表按 slot=CRC16(key)%16384 直连正确节点,落错节点时返回 MOVED 重定向让客户端更新槽表并重连;节点间 gossip 心跳互探做故障发现与槽表传播

数据分 16384 槽、每主一从;客户端按槽直连,落错返回 MOVED(永久迁移)或 ASK(迁移中临时),节点间 gossip 传播拓扑与故障。

跨槽限制(最常踩):多 key 操作、事务、Lua 脚本要求所有涉及的 key 落在同一个槽,否则报 CROSSSLOT。解法是 hash tag:给相关 key 加同一个 {...} 段,如 user:{123}:profileuser:{123}:budget——只有 {} 内的 123 参与 CRC16,于是同槽。

9. 拓扑选型与跨机房容灾

拓扑适用代价
单机纯缓存、数据能重建、量小无 HA、无扩展
主从 + Sentinel要 HA,数据量单机放得下有故障转移窗口;不解决容量/写吞吐
Cluster数据量/写吞吐超单机运维复杂、跨槽受限

代理方案(Codis、Twemproxy)曾用于在客户端不感知分片时做代理路由,如今多被原生 Cluster 与云托管替代,了解即可。

跨机房 / 容灾:单可用区不够时,把主从跨 AZ 部署(主 AZ-a、从 AZ-b),AZ 故障时切换;跨地域强一致的多活很难(异步复制 + 网络延迟),社区版一般做「一地写、异地灾备只读」,真要 Active-Active 多写常上企业版的 CRDT 方案。这块的 RTO/RPO、同城异地多活权衡,见 冗余与容灾高可用系统设计

10. AdTech 落地:竞价的 Redis HA

广告竞价把预算快照、频控计数、campaign 元数据放 Redis,读写都在 tmax(80–150ms)关键路径上,对「Redis 抖一下」零容忍。所以 HA 拓扑通常是 Cluster(按 campaign/user 分片)+ 每主一从 + 跨 AZ,且客户端侧一定叠加 L1 本地缓存兜底(见 本地缓存深挖)。

实践建议

参考


views
Share this post on:

Previous Post
Redis 深挖(三)· 线程模型与性能
Next Post
Redis 深挖(一)· 数据结构与内存模型