Appearance
持久化
Redis 以内存为数据主存位置,进程退出后内存数据就没了。持久化的目标是把内存数据保存到磁盘,重启时加载回来。它不能承诺"绝对不丢"——RDB 丢的是快照之后那一整段,AOF 取决于 fsync 策略也可能丢最后 1 秒。持久化是在"丢多少数据"和"写多少磁盘"之间找平衡。
一、RDB——时间点快照
RDB 是某个时间点的完整快照,生成一个紧凑的二进制文件(通常是 dump.rdb)。
配置:
conf
dir /data/redis
dbfilename dump.rdb
save 900 1 # 900 秒内至少 1 次写 → 触发
save 300 10 # 300 秒内至少 10 次写 → 触发
save 60 10000 # 60 秒内至少 10000 次写 → 触发save 规则之间是"或"的关系,任意一条满足就触发 BGSAVE。规则太少 → 快照间隔长 → 可能丢更多数据。规则太密集 → BGSAVE 频繁 → fork 和磁盘 IO 频繁影响性能。
手动触发生成 RDB:
bash
redis-cli BGSAVE # 后台 fork 子进程生成 RDB,不阻塞主线程
redis-cli SAVE # 前台生成,阻塞所有请求直到完成——生产不要用BGSAVE 的核心代价是 fork。fork 时 OS 需要为子进程创建虚拟地址空间(和父进程一样大),大内存实例上这个操作本身可能造成几百毫秒到秒级的延迟抖动。如果 vm.overcommit_memory 没设好或机器内存实际不够,fork 还可能直接失败。
查看 RDB 状态:
bash
redis-cli INFO persistence | grep -E 'rdb_last_bgsave_status|rdb_last_save_time|rdb_changes_since_last_save'| 字段 | 含义 |
|---|---|
rdb_last_bgsave_status | 最近一次 BGSAVE 是 ok 还是 err |
rdb_last_save_time | 最近一次成功保存的 Unix 时间戳 |
rdb_changes_since_last_save | 上次保存以来累积了多少写变更——如果这个数字很大且持续增长,说明最近没有成功快照 |
rdb_last_bgsave_status 是备份脚本要检查的字段——dump.rdb 文件存在不代表最近一次保存成功了,可能是一个星期前的旧文件。
二、AOF——写命令追加
AOF(Append Only File)把每次写命令追加到日志文件,Redis 重启时重放这些命令恢复数据。它比 RDB 更接近"实时记录",但数据丢失范围和 fsync 策略直接相关。
配置:
conf
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysecappendfsync 的三个级别:
| 策略 | 行为 | 丢数据范围 | 性能影响 |
|---|---|---|---|
always | 每条写命令都 fsync | 基本不丢 | 磁盘压力最大,QPS 明显下降 |
everysec | 每秒 fsync 一次 | 最多丢最后 1 秒 | 常用的平衡点 |
no | 交给 OS 决定何时刷 | 最多丢几十秒 | 性能最好,但丢失不可控 |
everysec 是生产环境最常见的选项——性能损耗可接受,丢失范围有上界。但"最多丢 1 秒"是理论值:如果磁盘本身慢,fsync 本身可能阻塞超过 1 秒,导致积压。
查看 AOF 状态:
bash
redis-cli INFO persistence | grep -E 'aof_enabled|aof_last_bgrewrite_status|aof_last_write_status|aof_current_size'aof_last_write_status 为 err 时,Redis 可能阻止写入(依赖 stop-writes-on-bgsave-error 的行为),需要立即查磁盘空间和权限。
Redis 7 的多文件 AOF
Redis 7 开始默认使用 multi-part AOF,不再是单个 appendonly.aof 文件。文件结构:
text
dir/appendonlydir/
├── appendonly.aof.manifest # 清单文件,记录当前 base 和 incr 文件
├── appendonly.aof.1.base.rdb # 重写后生成的基础文件(RDB 格式)
└── appendonly.aof.1.incr.aof # 重写后继续追加的增量命令配置目录名:
conf
appenddirname "appendonlydir"这个目录在 dir 之下。恢复和备份时,不能只操作单个 appendonly.aof 文件——manifest、base、incr 三者是成组的,缺一个都可能导致数据不完整。备份整个 appendonlydir 目录是最稳妥的做法。
三、AOF 重写——压缩历史命令
AOF 一直追加,文件会越来越大。比如同一个 key 被写了很多次:
text
SET cache:user:1 old
SET cache:user:1 new
SET cache:user:1 final
EXPIRE cache:user:1 300重写后只需要保留最终状态,不保留中间修改。AOF 重写就是生成一个更紧凑的新文件,把冗余命令压缩掉。
手动触发:
bash
redis-cli BGREWRITEAOF自动触发配置:
conf
auto-aof-rewrite-percentage 100 # AOF 比上次重写后增长 100% 时触发
auto-aof-rewrite-min-size 64mb # AOF 至少 64MB 才考虑触发AOF 重写也会 fork 子进程——和 BGSAVE 一样有 fork 延迟和内存压力的问题。排查 Redis 突然变慢时,可以先看是否有重写在跑:
bash
redis-cli INFO persistence | grep aof_rewrite_in_progress值为 1 表示重写正在进行中。如果实例很大、磁盘 IO 本来就高,重写期间延迟可能明显上升。
四、混合持久化
混合持久化是 AOF 重写时的优化:生成的新 AOF 文件前半段是 RDB 格式的快照,后半段是增量写命令。这样既保留了 AOF 的增量特性,恢复速度又接近 RDB:
conf
aof-use-rdb-preamble yesRedis 7 的多文件 AOF 里,base 文件默认就是 RDB 格式,实际上已经隐含了混合持久化的效果。
五、RDB 和 AOF 的加载顺序
同时开启 RDB 和 AOF 时,Redis 重启会优先加载 AOF——因为 AOF 通常包含更完整的数据。启动日志里能看到具体的加载过程:
bash
journalctl -u redis-6379 -n 100 --no-pager
tail -n 100 /var/log/redis/redis-6379.log如果 AOF 文件损坏,Redis 可能启动失败。检查工具:
bash
# 单文件 AOF(旧版或关闭多文件 AOF 时)
redis-check-aof --fix /data/redis/appendonly.aof
# 多文件 AOF(Redis 7 默认)
redis-check-aof --fix /data/redis/appendonlydir/appendonly.aof.manifest--fix 可能截断损坏的部分——执行前一定要先备份整个 AOF 目录或文件。截掉了什么、丢了哪些数据,需要在修复后对照业务确认。
RDB 文件检查:
bash
redis-check-rdb /data/redis/dump.rdb六、备份策略
RDB 文件是天然的备份对象——紧凑、单个文件、恢复速度快。
备份脚本的核心逻辑:触发 BGSAVE → 等待完成 → 确认状态为 ok → 复制文件:
bash
#!/bin/bash
set -euo pipefail
backup_dir="/backup/redis"
data_dir="/data/redis"
stamp="$(date +%F-%H%M%S)"
mkdir -p "$backup_dir"
# 触发后台快照
redis-cli BGSAVE
# 等待 BGSAVE 完成——避免复制到生成中的文件
while [ "$(redis-cli INFO persistence | awk -F: '/rdb_bgsave_in_progress/ {print $2}' | tr -d '\r')" = "1" ]; do
sleep 1
done
# 确认最近一次保存成功——文件存在不代表成功
status="$(redis-cli INFO persistence | awk -F: '/rdb_last_bgsave_status/ {print $2}' | tr -d '\r')"
if [ "$status" != "ok" ]; then
echo "rdb backup failed: $status" >&2
exit 1
fi
cp -a "$data_dir/dump.rdb" "$backup_dir/dump-$stamp.rdb"这个脚本检查了两样东西:rdb_bgsave_in_progress 确保快照完成,rdb_last_bgsave_status 确保快照成功。不检查状态,只看文件存在,可能备份的是一个星期前生成的老文件。
如果开启了 AOF,备份对象应该包括整个 appendonlydir 目录(Redis 7)或 AOF 文件(旧版)。AOF 的备份不能只靠 RDB 备份替代——AOF 的数据更新,恢复时更接近最新状态。
七、恢复流程
RDB 恢复:
bash
systemctl stop redis-6379
cp -a /data/redis /data/redis.bak.$(date +%F-%H%M%S) # 先备份当前目录
cp -a /backup/redis/dump-2026-05-22-010000.rdb /data/redis/dump.rdb
chown redis:redis /data/redis/dump.rdb
systemctl start redis-6379
redis-cli DBSIZE # 启动后确认 key 数量是否符合预期AOF 恢复同理,但要放回整个 appendonlydir 目录。关键陷阱:同时开启了 AOF 和 RDB 的实例,如果只替换了 RDB 文件但旧的 AOF 还在,Redis 启动时会优先加载 AOF——最终加载的可能不是备份文件里的数据,而是当前目录里残留的旧 AOF。替换 RDB 之前要确认:AOF 是不是也要跟着替换,或者暂时关闭 AOF 让 Redis 只加载 RDB。
八、持久化失败时的保护行为
RDB 后台保存失败后,Redis 有一个保护开关:
conf
stop-writes-on-bgsave-error yes这个配置的意思是:最近一次 BGSAVE 失败后,Redis 拒绝所有写入请求。设计意图是——"既然无法落盘,就不要让进程继续写新数据,否则重启后丢得更多"。但这对业务来说意味着 Redis 突然不可写。排查时看到写入报错,rdb_last_bgsave_status 和磁盘空间要同时看——很可能是磁盘满了。
如果 Redis 是纯缓存用途(数据可以从其他数据源重建),把这个开关关掉可以避免磁盘问题牵连缓存可用性。如果 Redis 存的是计数器、会话等需要持久化的数据,开着这个开关能更早暴露持久化异常。
九、几种场景的配置组合
| 场景 | 配置倾向 | 原因 |
|---|---|---|
| 纯缓存,数据能回源 | 可以不开持久化,或只开低频 RDB | 数据重建成本低,持久化不是必须 |
| 会话/限流/计数 | AOF everysec + RDB | 数据有状态,丢 1 秒在可接受范围 |
| 队列/状态类 | AOF everysec + 备份 + 主从 | 数据丢失影响较大 |
| 大内存实例(>32GB) | 关注 fork 耗时、重写窗口、磁盘吞吐 | fork 本身就是操作风险 |
持久化本质上是"降低丢失范围"的手段,不是数据库级别的最终保险。业务如果完全不能接受丢数据,设计上还要有数据库落地、消息确认或业务补偿机制——Redis 持久化只是多层保护中的一层。