5.3 持久化:RDB 与 AOF


5.3 持久化:RDB 与 AOF

Redis 把数据放在内存(5.1),代价就是断电即失——持久化机制就是给这个代价打的补丁。本节拆两种互补的持久化方式:RDB 快照与 AOF 日志,各自的实现原理、丢数据窗口与性能代价,最后给出一套按业务容忍度选择的决策流程。第 4.5 节 MongoDB 的写关注讨论在这里有镜像:Redis 同样要回答"确认到什么程度才算安全"。

RDB:某一刻的全量快照

RDB 把某一时刻的完整数据集写成紧凑的二进制文件。实现核心是 fork + 写时复制(COW):主进程 fork 出子进程,子进程遍历内存生成快照文件;fork 后父子进程共享内存页,只有被修改的页才复制副本——主进程继续服务、子进程安心写盘,几乎不互相干扰。

这个机制的三个推论决定了 RDB 的性格。快照是时间点:两次快照之间的写入不落在任何 RDB 里,宕机时丢的就是这段窗口(分钟级,取决于快照间隔)。fork 有瞬间成本:内存越大 fork 越慢(复制页表),且 COW 期间写入会放大内存占用,超大实例 + 大促写入高峰是 RDB 的危险组合。恢复快:二进制全量文件直接载入,百万键级也是秒到分钟级——比 AOF 重放快得多。

AOF:每一次写操作都记账

AOF 把每条写命令追加到日志文件,重启时重放全部命令恢复数据。写入策略由参数控制,三档对应三种丢数据窗口:

追加策略 语义 丢失窗口 性能
always 每命令刷盘 近乎零 差(每写一次磁盘 IO)
everysec(默认) 每秒刷盘 约 1 秒
no 交操作系统 不确定 最好,不可控

AOF 的膨胀问题由重写解决:AOF 文件里同一键被改了一万次就有一万条命令,重写进程按当前内存状态生成"每个键一条命令"的最小日志,体积骤降。4.0 起支持混合持久化:重写后 AOF 前半段是 RDB 格式的全量数据、后半段是增量命令——恢复速度接近 RDB、丢数据窗口维持秒级,两全其美,成为当前主流默认。

演练:一次宕机事故的恢复复盘

背景:订单状态服务用 Redis 存处理中状态(准生产配置:RDB 每小时一快 + AOF everysec),某日凌晨宿主机断电,Redis 进程未及优雅关闭。

操作:推演恢复过程。第一步重启实例,加载 AOF(含最后一次重写的 RDB 头 + 增量命令),最终恢复到断电前约 0.8 秒的状态;第二步盘点损失——断电前 1 秒内约 40 条状态更新丢失,其中 12 个订单的状态机停在中间态;第三步补偿——订单服务发现状态缺失的订单回到上游重发,状态机按幂等键去重;第四步决策升级——该业务对丢失容忍度为零,混合持久化 + 每秒刷盘仍不够,把关键状态的双写从 Redis 改为直接写 MongoDB(4.8 流水账模式),Redis 降级为纯加速层。

结果:服务 4 分钟内恢复;数据层面因架构调整,Redis 从此可随时重建,不再承担唯一事实源角色。

解读:这次复盘的价值在最后一步——持久化配置再极致也不能把 Redis 变成主存储:fork 风险、刷盘窗口、重写期间的性能抖动都是内存数据库的先天属性。正确架构是"主存储可重建 Redis",Redis 只承加速。丢 1 秒数据的配置在缓存场景毫无问题,在事实源场景就是事故,配置选择必须先问角色。

变式:若业务是会话类(丢了重新登录即可),RDB 单独就够(每小时快照 + 热备);若是要统计口径的计数类,everysec 的 AOF 合适——配置跟着丢数据容忍度走。

易错点

第一个是在高峰期手动触发全量快照或重写:fork 的页表复制与 COW 内存放大叠加写入高峰,实例卡顿甚至内存溢出;重活安排在低谷期。第二个是只配持久化不配主从:单机持久化保护不了主机硬件故障,持久化 + 主从(5.7)才是完整方案,且从库重放的是主库同步的操作流,二者互补。第三个是忽视 AOF 重写期间的写入放大:重写时主进程的写命令同时写 AOF 缓冲与重写缓冲,极端写入下内存翻倍,监控内存要留余量。第四个是误信"开了持久化就不丢":always 档也有进程崩溃时缓冲未刷的毫秒级窗口,任何承诺都要按丢失窗口换算。

三种持久化配置对照

方案 原理 数据安全性 对性能的影响 恢复速度
RDB 快照 定时把内存数据整体落盘 丢最后一次快照之后的写入 子进程 fork 时有短暂停顿 快,直接加载
AOF 日志 记录每条写命令,重启时重放 取决于刷盘策略(通常丢 1 秒) 写放大,文件持续增长 慢,要重放命令
混合持久化 AOF 文件里包含 RDB 格式的全量 + 增量命令 同 AOF 介于两者之间 快于纯 AOF

配置片段与含义

# RDB:900 秒内至少 1 次写入则触发快照;可配置多组条件 save 900 1 save 300 10 save 60 10000 # AOF:开启后每条写命令追加到日志;刷盘策略三选一 appendonly yes appendfsync everysec # 每秒刷盘(默认,折中);always 最安全最慢;no 最快最不安全 # 混合持久化(4.0 起):AOF 重写时以 RDB 格式写前半段,兼顾速度与体积 aof-use-rdb-preamble yes

刷盘策略的选择是唯一需要认真决策的地方:always 每条命令都刷盘,几乎不丢数据但吞吐明显下降;everysec 每秒刷盘,最多丢一秒,是绝大多数业务的选择;no 交给操作系统决定,最快但不可控。

演练:误删数据后的恢复流程

场景:执行了 FLUSHDB,需要把某个业务的数据恢复到误操作前。

# 1) 立即停止写入,防止 AOF 继续追加(否则重写会覆盖掉恢复机会) redis-cli SHUTDOWN NOSAVE # 若只开了 RDB,千万别用带保存的关闭方式 # 2) 找到最近的 RDB 快照或 AOF 文件,复制到安全的临时目录 cp /var/lib/redis/dump.rdb /data/rescue/dump.rdb # 3) 在另一台机器上用这份文件启动一个临时实例(端口与生产隔离) redis-server --port 6399 --dir /data/rescue --dbfilename dump.rdb # 4) 从临时实例导出需要的 Key,核对后回灌生产 redis-cli -p 6399 --scan --pattern 'order:*' | head -1000 > keys.txt # 逐 Key 用 DUMP + RESTORE 迁移(RESTORE 可指定目标实例)

三条血的教训:

  • FLUSHDB 与 FLUSHALL 应当在生产上禁用或改名。Redis 支持通过配置把危险命令重命名成随机字符串,这是成本最低的防护;
  • 备份文件必须与生产实例异地保存。只放在同一块磁盘上的 RDB,在磁盘故障时与数据一起消失;
  • 定期演练恢复。没验证过的备份不算备份——至少要每季度在临时实例上完整恢复一次,确认文件没有损坏、恢复耗时在可接受范围内。

本节要点回顾

  • RDB = 时间点全量快照:fork + 写时复制实现,恢复快、丢窗口分钟级。
  • AOF = 写命令流水账:everysec 是常规档(丢约 1 秒),重写机制控制体积。
  • 混合持久化(RDB 头 + AOF 增量)是当前主流,兼顾恢复速度与丢失窗口。
  • fork 成本与 COW 内存放大随实例体积增长,大实例快照要避峰
  • 配置跟着角色走:加速层可重建,事实源别用 Redis 当

数据保住了,下一节进入生产 Redis 的主战场:缓存怎么用才不出事故。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U