Redis 持久化:RDB 快照与 AOF 日志

一、为什么需要持久化

Redis 所有数据在内存中,进程退出 / 宕机 / 掉电 = 数据全丢。但很多场景(缓存、会话、排行榜)又要求重启后数据还在,所以 Redis 提供两种持久化机制:

方案 原理 特点
RDB 定期把内存全量快照写磁盘 二进制、恢复快、可能丢数据
AOF 把每条写命令追加到日志 文本协议、数据安全、文件大、恢复慢

二、RDB 快照

2.1 是什么

在某个时间点,把 Redis 内存中的全量数据 生成一个二进制快照文件(默认 dump.rdb)。重启时加载这个文件恢复数据。

2.2 手动触发:save 与 bgsave

复制代码
127.0.0.1:6379> SAVE        # 主进程同步执行,阻塞所有客户端,直到快照完成
127.0.0.1:6379> BGSAVE      # fork 子进程后台执行,主进程继续服务
命令 执行方式 阻塞主进程? 生产使用
SAVE 主进程直接写盘 ✅ 全程阻塞 ❌ 几乎不用
BGSAVE fork 子进程写盘 fork 瞬间短暂阻塞 ✅ 推荐

2.3 fork + 写时复制(COW)原理

复制代码
父进程(Redis 主进程)          子进程(BGSAVE)
       │                            │
       ├─ fork() ──────────────────▶│
       │  共享内存页                │ 拿到当时内存快照
       │                            │
       ├─ 继续处理客户端写命令      │
       │  修改某内存页              │
       │  ├─ COW 触发:             │
       │  │  复制那一页到自己       │
       │  │  子进程仍看旧页         │
       │  ▼                         ▼
       │  主进程用新页              子进程写旧页到 dump.rdb
       ▼
  • fork 瞬间:父子进程共享同一份内存页(页表指向同一物理页);
  • 写时复制(Copy-On-Write):主进程修改某一页时,操作系统才把那一页复制一份,子进程看到的还是旧快照;
  • 所以子进程写的是fork 那一刻的一致性快照,主进程期间的新数据不影响快照。

隐藏成本:fork 本身要复制整个内存页表,数据量越大(比如 20GB),fork 阻塞越久------这是 BGSAVE 在大内存下的痛点。

2.4 自动触发

复制代码
# redis.conf
save 900 1      # 900 秒内至少 1 次修改 → 触发 BGSAVE
save 300 10     # 300 秒内至少 10 次修改 → 触发
save 60 10000   # 60 秒内至少 10000 次修改 → 触发

另外:执行 SHUTDOWN 且配置了 save 规则时,会自动触发一次 RDB。

2.5 工作流程

复制代码
子进程写临时文件 temp-*.rdb
   │
   ▼ 写完
父进程原子 rename:temp-*.rdb → dump.rdb(替换旧文件)

不直接覆盖 dump.rdb,先写临时文件再 rename------避免写一半断电导致原文件损坏。

2.6 文件位置与校验

复制代码
dir /var/lib/redis        # 快照目录(redis.conf 的 dir 变量)
dbfilename dump.rdb       # 快照文件名

文件损坏时用工具检查:

复制代码
redis-check-rdb /var/lib/redis/dump.rdb
# 路径:/usr/bin/redis-check-rdb(与 redis-server 同目录,本质是同一个二进制的软链接)

2.7 RDB 优缺点

优点 缺点
二进制紧凑,恢复快 两次快照之间宕机丢数据
fork 子进程,主进程阻塞小 大内存 fork 瞬间阻塞明显
适合冷备份、全量复制 不能做任意时间点恢复(PITR)

三、AOF 追加日志

3.1 是什么

AOF(Append Only File)以 RESP 文本协议记录每一条写命令,重启时重放这些命令重建数据。类比 MySQL 的 binlog。

复制代码
# 一条 set 命令在 AOF 里长这样(纯文本)
*3
$3
SET
$3
key
$5
value

3.2 开启方式

复制代码
appendonly yes          # 默认 no(Redis 默认只用 RDB)
appendfilename "appendonly.aof"

加粗要点 :同时开 RDB + AOF 时,重启加载优先用 AOF(因为 AOF 数据更全)。

3.3 三种 fsync 策略

在记录的时候,显示保存在aof_buffer缓冲区中,根据刷盘策略的不同,落盘时间有差异。

复制代码
appendfsync everysec    # 默认 everysec
策略 行为 安全性 性能
always 每条命令写盘后立刻 fsync 最安全,最多丢 0 条 最慢
everysec write 到 OS 缓冲,每秒由后台线程 fsync 可能丢 1 秒数据 折中,推荐
no 只 write,fsync 交给 OS 自己决定 可能丢很多 最快

3.4 AOF 重写(BGREWRITEAOF)

AOF 持续追加,文件会越来越大,而且有大量冗余命令:

复制代码
set k 111
set k 222
set k 333
→ 重写后只需要:set k 333

127.0.0.1:6379> BGREWRITEAOF    # 手动触发

重写不是读旧 AOF 命令合并,而是子进程直接遍历当前内存数据,生成最小命令集。(这一点和RDB快照 相同)

3.5 双 buffer 机制

复制代码
fork 瞬间:
  父进程 ──▶ 子进程(拿内存快照,开始写新 AOF 临时文件)
                │
                ▼
         子进程遍历内存写新 AOF
                │
fork 之后主进程继续执行新命令:
  每条新命令同时写两个 buffer:
    ├─ aof_buffer         → 正常刷旧 AOF 文件(保证宕机时旧 AOF 连续可用)
    └─ aof_rewrite_buffer → 等子进程写完后,由父进程追加到新 AOF 尾部
                │
                ▼
         子进程写完 → 发信号给父进程
         父进程把 rewrite_buffer 内容追加到新 AOF 尾部
         原子 rename 新文件替换旧 AOF

两个 buffer 的分工:

buffer 作用
aof_buffer 主进程一直用,写旧 AOF 文件,保证重写期间宕机也不丢数据
aof_rewrite_buffer fork 之后的新命令额外抄一份,等子进程写完后补到新 AOF 尾部

3.6 自动重写触发

复制代码
auto-aof-rewrite-percentage 100   # 当前 AOF 比上次重写后增大 100%(翻倍)
auto-aof-rewrite-min-size 64mb    # 且文件大于 64MB 才触发(避免小文件重写)

含义:AOF 文件比上次重写时增长超过 100%,且超过 64MB,自动 BGREWRITEAOF。


四、混合持久化(Redis 4.0+)

AOF 本身是纯文本 RESP。但 4.0 引入了混合持久化:

复制代码
aof-use-rdb-preamble yes    # 4.0+ 默认开启

开启后,AOF 重写时:

复制代码
新 AOF 文件结构:
┌──────────────────────────┬──────────────────────────┐
│ 前半段:RDB 二进制全量快照  │ 后半段:RESP 文本增量命令  │
└──────────────────────────┴──────────────────────────┘
       ↑ 加载快                       ↑ 数据安全
  • 前半段 RDB:二进制格式,重启加载快;
  • 后半段 AOF:fork 之后的增量命令,保留秒级数据安全;
  • 兼顾 RDB 的恢复速度和 AOF 的数据完整性。

五、RDB vs AOF vs 混合

维度 RDB AOF 混合持久化
数据格式 二进制 RESP 文本 前 RDB 后 AOF
数据安全 可能丢几分钟 everysec 最多丢 1 秒 最多丢 1 秒
恢复速度 快 慢(重放命令) 较快
文件体积 小 大 中等
适用场景 冷备份、主从复制 数据安全要求高 生产推荐

启动加载顺序:AOF 开启 → 优先加载 AOF(或混合文件);否则加载 RDB。

相关推荐
.痞老板.37 分钟前
mysql初阶--数据库设计
数据库·mysql·oracle
Decay__2 小时前
SQL Server 数据库全栈运维实战系列 — 合集索引(24 篇)
运维·数据库·sql·mysql·架构
Thanks_ks2 小时前
SQL 的 GROUP BY 与聚合函数:从会写 SELECT 到会做统计
数据库·sql·mysql·group by·数据统计·聚合函数·having
IT规划师2 小时前
Redis 数据类型全解 - Stream 深度:消费者组、Pending 队列与死信处理(十)
redis·.net
Decay__10 小时前
12_MySQL InnoDB 内存与锁调优 —— Buffer Pool、redo log、隔离级别与锁等待
运维·数据库·sql·mysql·架构
笃行35011 小时前
AI数据库的库内四层结构,KingbaseES 产品拆解
数据库
我是大猴子13 小时前
redis
数据库·redis·缓存
苏宸啊15 小时前
MySQL(一)
数据库·mysql
2603_9696948515 小时前
JDBC 基础学习笔记:API 核心原理解析与 CRUD 代码实操
数据库·笔记·mysql·学习方法·database·sql注入