Skip to content

持久化:RDB 与 AOF 怎么选 ​

这个配置决定的是宕机时最多丢多少数据。选错不是"性能差一点",是丢单、丢消息、丢用户操作记录。

场景:先想清楚能丢多少 ​

业务能接受的数据丢失选择
纯缓存,能回源数据库丢 5 分钟无所谓只开 RDB
缓存 + 少量计数/会话丢 1 秒RDB + AOF everysec
队列、订单状态、分布式锁审计一条都不能丢AOF always + 主从 + 定期备份
只做排行榜、可重算全丢也能重建可以关持久化,但要写进文档

Redis 7 的默认配置是 RDB 开、AOF 关,也就是最弱的一档。所以线上第一件事是确认这两项。

一、看清当前状态 ​

bash
redis-cli -a '密码' config get save
redis-cli -a '密码' config get appendonly
redis-cli -a '密码' config get appendfsync
redis-cli -a '密码' info persistence

输出:

text
1) "save"
2) "3600 1 300 100 60 10000"
1) "appendonly"
2) "no"
1) "appendfsync"
2) "everysec"

# Persistence
loading:0
async_loading:0
current_cow_peak:0
current_cow_size:0
current_fork_perc:0.00
rdb_changes_since_last_save:137
rdb_bgsave_in_progress:0
rdb_last_save_time:1761549124
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:1
rdb_current_bgsave_time_sec:-1
rdb_saves:42
rdb_last_cow_size:18579456
rdb_last_load_keys_expired:0
rdb_last_load_keys_loaded:18234
aof_enabled:0

# AOF
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_bgrewrite_status:ok
aof_rewrites:0
aof_last_write_status:ok

解释:

  • save "3600 1 300 100 60 10000" 是三个快照点:3600 秒内 1 次写、300 秒内 100 次写、60 秒内 10000 次写,满足任一条件就 fork 一次 BGSAVE。
  • rdb_changes_since_last_save:137 是当前"还没落盘"的写次数,也就是此刻宕机会丢的量。
  • rdb_last_bgsave_status:ok 必须盯,变成 err 通常是磁盘满。
  • rdb_last_cow_size 是 fork 期间因写时复制额外占的内存,几十 MB 正常,上 GB 说明 BGSAVE 期间写入太猛。
  • Redis 7 把 AOF 相关字段挪到了 # AOF 段,aof_enabled 仍留在 Persistence 段。

二、RDB:快照 ​

bash
# 手动触发(后台 fork,不阻塞)
redis-cli -a '密码' bgsave
redis-cli -a '密码' lastsave        # 返回上次成功落盘的 Unix 时间戳

# 阻塞式,生产禁用
# redis-cli -a '密码' save

输出:

text
Background saving started
1761549432

文件落在 dir 指定的目录:

bash
ls -lh /var/lib/redis/6379/
text
-rw-r--r-- 1 redis redis  21M Oct 27 17:07 dump.rdb

关键配置:

ini
# 关掉自动快照,只在低峰用 cron 触发(写放大敏感的场景)
save ""

# 打开快照
save 900 1
save 300 10
save 60 10000

# 压缩:省磁盘,CPU 略增,字符串多的场景收益明显
rdbcompression yes

# 校验和,损坏能早发现
rdbchecksum yes

# 快照失败时是否拒绝写入。默认 yes
stop-writes-on-bgsave-error yes

# 子进程 fork 失败或写盘失败时,多久后重试
rdb-del-sync-files no

stop-writes-on-bgsave-error 是双刃剑

默认 yes 时,磁盘满导致 BGSAVE 失败,Redis 会开始拒绝所有写命令并返回 MISCONF Redis is configured to save RDB snapshots...。这是故意的,防止你以为在写其实数据早就没落盘。正确处置是修磁盘,不是把它改成 no。

RDB 的两个硬伤:

  • fork 阻塞。BGSAVE 本身不阻塞,但 fork 那一刻要复制页表;堆越大、页越碎,阻塞越久。32G 堆在普通云盘上 fork 能卡 200ms~1s。
  • 丢最后一个窗口。两次快照之间的写全在内存里。

三、AOF:命令日志 ​

bash
redis-cli -a '密码' config set appendonly yes
redis-cli -a '密码' config rewrite

输出:

text
OK
OK

Redis 7 的 AOF 不再是单个文件,而是一个目录:

bash
ls -lh /var/lib/redis/6379/appendonlydir/
text
-rw-r--r-- 1 redis redis 4.0K Oct 27 17:12 appendonly.aof.1.base.rdb
-rw-r--r-- 1 redis redis  512 Oct 27 17:12 appendonly.aof.1.incr.aof
-rw-r--r-- 1 redis redis   88 Oct 27 17:12 appendonly.aof.manifest

解释:base 是重写后的基线(aof-use-rdb-preamble yes 时是 RDB 格式,加载更快),incr 是增量追加,manifest 记录当前用了哪些分片。多了 multi-part AOF 这层,重写期间不会出现"旧文件删了、新文件没写完"的空窗。

appendfsync 三档怎么选:

值行为丢数据性能
always每条命令都 fsync最多丢 1 条吞吐降到 1/5 以下
everysec每秒 fsync 一次(默认)最多丢 1 秒几乎无损
no交给内核刷最多丢 30 秒最快,但不可控

99% 的场景用 everysec。always 只在"丢一条就是事故"时用,且要接受 QPS 掉一个数量级。

AOF 常用配置:

ini
appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite no        # 重写期间不 fsync 会更稳但风险高,保持 no

auto-aof-rewrite-percentage 100     # 比上次重写后体积翻倍就重写
auto-aof-rewrite-min-size 64mb      # 小于 64M 不重写

aof-load-truncated yes              # 末尾半条命令忽略并记日志,而不是拒绝启动
aof-use-rdb-preamble yes            # 7.0 默认

手动重写并观察:

bash
redis-cli -a '密码' bgrewriteaof
redis-cli -a '密码' info persistence | grep -E 'aof_rewrite_in_progress|aof_last_bgrewrite_status|aof_last_cow_size'

输出:

text
Background append only file rewriting started
aof_rewrite_in_progress:0
aof_last_bgrewrite_status:ok
aof_last_cow_size:9437184

别在业务高峰做 BGREWRITEAOF

它同样要 fork 子进程,且子进程会重放整个数据集写新文件,磁盘 IO 会顶满。放到凌晨,或者依赖 auto-aof-rewrite 的阈值自然触发。

四、重启时到底加载谁 ​

顺序是固定的:

  1. appendonly yes → 只加载 AOF(目录里的 manifest 指向 base + incr)。
  2. appendonly no → 加载 dump.rdb。
bash
tail -20 /var/log/redis/6379.log
text
* DB loaded from append only file: 0.412 seconds
* Ready to accept connections tcp

如果日志出现 DB loaded from disk: 0.231 seconds,说明走的是 RDB。

这就是最容易踩的坑:你后来开了 AOF,但数据是之前 RDB 里的;或者从库也开了 AOF,重启后加载了自己那份旧的 AOF,于是从库数据和主库对不上。

五、备份:把文件拿走才叫备份 ​

RDB 只在同一台机器上,机器没了数据就没了。

bash
#!/bin/bash
# /usr/local/bin/redis-backup.sh
set -euo pipefail
DATE=$(date +%F_%H%M)
DIR=/var/lib/redis/6379
DEST=/data/backup/redis

redis-cli -a "$REDIS_PASS" bgsave >/dev/null
while [ "$(redis-cli -a "$REDIS_PASS" info persistence | awk -F: '/rdb_bgsave_in_progress/{print $2}' | tr -d '\r')" != "0" ]; do
  sleep 1
done

gzip -c "$DIR/dump.rdb" > "$DEST/dump-$DATE.rdb.gz"
find "$DEST" -name 'dump-*.rdb.gz' -mtime +7 -delete
text
20 3 * * * REDIS_PASS='密码' /usr/local/bin/redis-backup.sh >> /var/log/redis/backup.log 2>&1

解释:

  • 必须先 bgsave 再拷,否则拷到的是残缺文件。
  • 等 rdb_bgsave_in_progress 变 0 再拷,避免读一个正在写的文件。
  • gzip 对 RDB 压缩率通常在 3~5 倍。
  • 保留 7 天,异地再同步一份才算真备份。

六、恢复演练 ​

bash
systemctl stop redis-6379
cp /data/backup/redis/dump-2026-10-27_0320.rdb.gz /tmp/
gunzip /tmp/dump-2026-10-27_0320.rdb.gz
chown redis:redis /tmp/dump-2026-10-27_0320.rdb

# 先校验再覆盖,别直接盖
redis-check-rdb /tmp/dump-2026-10-27_0320.rdb

mv /var/lib/redis/6379/dump.rdb /var/lib/redis/6379/dump.rdb.bak
mv /tmp/dump-2026-10-27_0320.rdb /var/lib/redis/6379/dump.rdb
rm -rf /var/lib/redis/6379/appendonlydir      # 开了 AOF 就必须先挪走 AOF

systemctl start redis-6379
redis-cli -a '密码' dbsize

输出:

text
[offset 0] Checking RDB file /tmp/dump-2026-10-27_0320.rdb
[offset 27] AUX FIELD redis-ver = '7.2.5'
[offset 201] AUX FIELD ctime = '1761525600'
[offset 234] Selecting DB ID 0
[offset 18234] Checksum OK
[offset 18234] \o/ RDB looks OK! \o/
[info] 1 keys read
[info] 0 expires
[info] 0 already expired

恢复时忘了挪走 AOF

appendonly yes 的情况下 Redis 只读 AOF。你把新 RDB 拷进去,看到 dbsize 还是旧数据,会以为备份是坏的。恢复前必须把 appendonlydir 整体移走。

七、踩过的坑 ​

现象根因处置
写命令全部 MISCONFBGSAVE 失败(磁盘满/无权限)df -h、查 dir 权限,别关 stop-writes-on-bgsave-error
启动报 Short read or OOM loading DBRDB 文件损坏(拷贝时正在写)用备份恢复,redis-check-rdb 确认
启动报 Bad file format reading the append only fileAOF 尾部截断(断电)redis-check-aof --fix 修,再起
重启后数据变回几小时前开了 AOF 但 AOF 目录是空的/旧的确认 appendonlydir 内容与时间戳
每秒卡顿一次appendfsync always 或磁盘 fsync 慢改 everysec,换 SSD
内存突然涨 1 倍fork 期间 COW降低写入峰值,或换更大内存/关 THP
从库数据比主库旧很多从库自己开了 AOF 且重启加载本地文件从库 appendonly no,或重建从库