Skip to content

第六章 持久化:数据不丢失的秘密

前几章我们学会了用 Redis 存数据、查数据。但有一个现实问题:Redis 把数据放在内存里,进程一停、机器一重启,内存里的东西就没了。这一章讲 持久化(Persistence):怎样把内存里的数据安全地落到磁盘上,重启后还能找回来。


6.1 为什么需要持久化

Redis 是内存数据库,重启数据就没了

  应用写入 Redis
       |
       v
  +------------------+
  |     内存 (RAM)    |  <-- 数据在这里,速度快
  +------------------+
       |
       |  若未做持久化:进程退出 / 断电 / 重启
       v
  +------------------+
  |   数据全部丢失    |
  +------------------+
  • 内存读写快,但易失:断电、崩溃、正常关机后,未保存到磁盘的数据都会消失。
  • 很多场景(缓存可以丢、主数据在别处)可以接受丢一部分;但若 Redis 里放的是会话、计数、队列等不能丢的数据,就必须做持久化。

持久化 = 把内存数据保存到磁盘

持久化的含义可以概括成一句话:

在适当时机,把内存中的数据以某种格式写入磁盘文件;下次启动时 Redis 读取这些文件,尽量恢复到关机前的状态。

Redis 主要提供两种方式(以及后面的混合模式):

方式核心思路
RDB某一时刻的快照(整库或按策略触发)
AOF记录写命令的日志,重放即可恢复

下面分别展开,并用 ASCII 图帮助理解流程。


6.2 RDB 快照

原理:定时将内存数据生成快照文件(dump.rdb)

RDB(Redis Database)把某一时刻的内存数据序列化后写成一个二进制文件,默认 often 名为 dump.rdb

  时间轴上的“拍照”
  ========================>

  t1          t2          t3
  |           |           |
  v           v           v
 [快照]      [快照]      [快照]
 dump.rdb   (覆盖或轮转)  ...

直观理解:像给数据库拍一张全景照,恢复时相当于“回到拍照那一刻”的状态,拍照之间的增量变化若没有别的机制,可能会丢。

工作流程(ASCII)

                    RDB 生成流程(概念)
  +-------------------------------------------------------------+
  |  Redis 主进程                                                |
  |  +------------------+                                        |
  |  | 内存中的键值数据  |                                        |
  |  +--------+---------+                                        |
  |           |                                                  |
  |           | 触发 SAVE / BGSAVE / 满足 save 配置               |
  |           v                                                  |
  |  +------------------+      fork()      +------------------+ |
  |  | 可选:子进程路径   | ---------------> | 子进程            | |
  |  +------------------+                  | 遍历内存写磁盘    | |
  |           |                            +--------+-----------+ |
  |           |                                     |             |
  |           | 主进程仍可处理读请求;                v             |
  |           | 写请求在 COW 下复制改动的页          |             |
  |           v                                     v             |
  |     继续服务客户端                      写入 dump.rdb         |
  +-------------------------------------------------------------+

触发方式

1. 手动命令

命令行为简述
SAVE阻塞主进程,直到快照写完。生产环境慎用。
BGSAVE后台保存:fork 子进程写 RDB,主进程继续服务。

2. 配置自动触发(redis.conf)

通过 save 指令定义“在 N 秒内至少发生 M 次写”就自动 BGSAVE:

conf
# 900 秒内至少 1 次写
save 900 1
# 300 秒内至少 10 次写
save 300 10
# 60 秒内至少 10000 次写
save 60 10000

多条 save 的关系:满足任意一条就会触发一次快照(实现上多为 BGSAVE)。

3. 其他

  • 正常关闭 Redis(如 SHUTDOWN)时,常会尝试生成一次 RDB(取决于配置与版本行为)。
  • 主从复制等场景也可能间接触发 RDB 传递。

fork 子进程与 Copy-On-Write(简要说明)

BGSAVE 时,Redis 主进程会 fork() 出一个子进程

  fork 之后瞬间
  +----------------+     +----------------+
  | 父进程(Redis) |     | 子进程          |
  | 虚拟地址空间    |     | 与父共享同一套  |
  | 指向相同物理页  |     | 物理内存页(只读)|
  +----------------+     +----------------+
           |                       |
           | 父进程继续处理写请求    | 子进程只读内存写 RDB
           v                       v
  写某 key 时:复制被改动的内存页    读到的仍是 fork 时刻视图
  (Copy-On-Write)
  • Copy-On-Write(写时复制):fork 后父子先共享物理页;父进程要修改某一页时,内核复制该页,父进程写新页,子进程仍读旧页。这样子进程看到的数据相当于 fork 瞬间 的快照。
  • 注意:若写入非常频繁,COW 会导致复制很多页,内存与 fork 耗时都会上升,可能抖动甚至 OOM,这是 RDB/BGSAVE 在高压写场景下的经典风险点。

RDB 优缺点

优点缺点
文件紧凑、适合备份与全量恢复两次快照之间的数据可能丢失(宕机)
恢复大数据集时通常比重放 AOF 快fork 在数据量大或写多时成本高
对客户端影响相对可控(BGSAVE)不适合要求“每条写都落盘”的强一致场景

6.3 AOF 日志

原理:将每条写命令追加到 appendonly.aof 文件

AOF(Append Only File)把成功的写命令按 Redis 协议格式追加到日志文件(默认 appendonly.aof)。重启时 Redis 从头到尾执行一遍这些命令,重建数据集。

  写命令流
  ========>

  SET k1 v1  ----\
  INCR counter   +-->  追加写入 appendonly.aof
  DEL oldkey    ----/

  重启时:顺序重放 AOF -> 内存状态恢复

工作流程(ASCII)

                    AOF 持久化(概念)
  +-------------------------------------------------------------+
  |  客户端          Redis 主进程                                 |
  |    |                  |                                       |
  |    | 写命令            |                                       |
  |    v                  v                                       |
  |              +----------------+                               |
  |              | 执行写操作      |                               |
  |              | 修改内存       |                               |
  |              +-------+--------+                               |
  |                      |                                        |
  |                      | 根据 appendfsync 策略                 |
  |                      v                                        |
  |              +----------------+                               |
  |              | 缓冲区 -> 刷盘  | --> appendonly.aof 文件        |
  |              +----------------+                               |
  +-------------------------------------------------------------+

开启 AOF(redis.conf)示例:

conf
appendonly yes
appendfilename "appendonly.aof"

三种刷盘策略:appendfsync

取值含义数据安全性能
always每个写命令后 fsync最高,几乎不丢已确认的写最慢
everysec每秒 fsync 一次(默认)最多丢约 1 秒 内的写平衡,常用
no交给操作系统决定何时刷盘可能丢更多缓冲中的数据最快,风险大

配置示例:

conf
# always | everysec | no
appendfsync everysec

AOF 重写机制(BGREWRITEAOF

AOF 会越写越长,很多中间状态其实可以合并成更少命令(例如对同一 key 多次修改,最终只保留最后结果)。

AOF 重写:生成一份新的、更精简的 AOF,用更少的命令表达当前内存状态

  旧 AOF(很长)              重写后 AOF(更短)
  SET a 1                     SET a 3
  SET a 2         ----->      SET b 1
  SET a 3                     ...
  ...                         (等价于当前数据)
  • 可自动触发(根据 auto-aof-rewrite-percentageauto-aof-rewrite-min-size 等配置)。
  • 也可手动:BGREWRITEAOF(后台重写)。

重写同样常用 子进程,期间新写入会同时记入 AOF 重写缓冲区,最后合并,保证不丢这段时间的写(细节实现随版本演进,初学者掌握“瘦身 + 后台”即可)。

AOF 优缺点

优点缺点
默认 everysec 下,耐久性明显好于“纯 RDB 间隔快照”文件通常比 RDB 大,恢复要重放命令可能较慢
可读性相对好(文本协议),便于部分排错刷盘策略选错会明显影响延迟或丢数据窗口
适合更细粒度的持久化需求重写期间仍有额外 CPU/磁盘开销

6.4 RDB vs AOF 对比

对比表

对比项RDBAOF
存储内容某一时刻内存的快照(二进制)写命令日志
默认是否开启通常默认开启或易通过 save 触发appendonly yes
丢数据风险两次快照之间宕机可能丢较多everysec 下通常最多约 1 秒
恢复速度大数据集往往 更快日志很长时可能 较慢
文件体积一般 更小易膨胀,需 重写
磁盘压力周期性集中写入持续追加 + 定期重写

选择建议(初学者版)

  1. 既要快恢复、又要少丢数据:生产常见做法是 同时开 RDB + AOF(或下一节的混合持久化),用 AOF 保耐久性,用 RDB 做备份/快速恢复辅助(具体以运维策略为准)。
  2. 能接受丢几分钟数据、只要简单备份:可侧重 RDB,调大 save 间隔换性能(丢得更多)。
  3. 几乎不能丢写:AOF + appendfsync always(性能代价大);或配合业务层双写/消息队列,不仅依赖 Redis 单机配置。
  4. 纯缓存、丢了能重建:可以关持久化或极宽松 RDB,但要明确业务能容忍

6.5 混合持久化(Redis 4.0+)

RDB + AOF 混合:取两者之长

混合持久化下,AOF 文件可以是:前半段为 RDB 格式的快照数据后半段为增量 AOF 命令。重启时先加载 RDB 部分快速构建大部分数据,再重放后面的 AOF 增量,兼顾恢复速度与耐久性

  混合 AOF 文件(示意)
  +------------------------+------------------------+
  | RDB 格式快照(紧凑)    | 增量 AOF 命令          |
  +------------------------+------------------------+
           |                          |
           +-------- 重启时先加载 RDB,再重放 AOF ---+

配置方式

redis.conf 中(Redis 4.0+,具体选项名以你所用版本文档为准):

conf
appendonly yes
appendfsync everysec

# 开启混合持久化(Redis 4.0+)
aof-use-rdb-preamble yes

提示:不同大版本配置项可能略有调整,上线前请对照官方文档与你当前的 redis.conf 模板。


本章小结

小节核心要点
6.1 为什么持久化内存易失;持久化把数据落到磁盘以便重启恢复。
6.2 RDB定时/手动快照 → dump.rdbBGSAVE + fork + COW;快、紧凑,但可能丢两次快照间数据。
6.3 AOF追加写命令;always / everysec / no 控制刷盘;BGREWRITEAOF 瘦身日志。
6.4 对比与选择RDB 偏备份与快速全量恢复;AOF 偏少丢数据;生产常组合使用。
6.5 混合持久化AOF 内嵌 RDB 头 + 增量 AOF,平衡恢复速度与数据安全。

下一章预告

第七章:常见应用场景实战——将把前面学过的数据结构、持久化与性能意识结合起来,用贴近业务的例子(如缓存、会话、限流、排行榜等)走一遍怎么设计键、怎么选型、要注意哪些坑,让你在“会用命令”的基础上,进一步做到“会用在项目里”。

坚持是一种品格