Redis 持久化实战:RDB 与 AOF 怎么选,附混合持久化配置与一次数据丢失复盘

为什么你的 Redis 会突然丢数据:从一次真实的故障说起

那是一个很普通的周二凌晨,我运营的一个个人站后台开始报"用户登录状态全部失效",紧接着缓存里的排行榜数据也回到了三天前的状态。运维群里第一反应是"被攻击了",但我登录服务器一看,Redis 进程活得好好的,内存占用正常,端口也在监听。真正的原因,是我三个月前图省事,把 Redis 的持久化配成了默认的 RDB 快照,而这份快照文件的最后一次写入时间是三天前——也就是说,这三天写入的所有数据,在一次意外的进程重启后全部蒸发了。

这件事让我痛下决心,把 Redis 的持久化机制从头到尾捋了一遍。这篇文章就把 RDB、AOF 两种持久化方式的原理、配置、取舍,以及混合持久化的正确用法讲清楚,最后附上那次事故后的完整整改配置。如果你也把 Redis 当成"内存缓存,丢了就丢了",那至少要把它的边界搞清楚,因为很多人的 Redis 里其实混着不该丢的东西。

先搞懂一件事:Redis 的持久化到底在防什么

很多人对 Redis 有个误解,觉得它是纯内存数据库,重启数据就没了,所以持久化"没啥意义"。这个理解只对了一半。

Redis 的数据确实常驻内存,但它的持久化机制是把内存里的数据落到磁盘上,这样进程意外崩溃、服务器重启、甚至误操作 FLUSHALL 之后,都有机会通过磁盘文件把数据恢复回来。持久化解决的不是"数据一直在内存里"的问题,而是"内存里的数据能不能在进程生命周期之外活下来"的问题。

关键在于,Redis 的持久化不是二选一的简单开关,它有两套独立的机制:RDB(快照)和 AOF(追加日志)。两者可以单独用,也可以同时用,而它们的组合方式直接决定了你能承受多大的数据丢失。

RDB 快照:把某一刻的内存拍成照片

RDB 的原理很好理解:Redis 在某个时间点,把内存中所有键值对序列化成二进制文件,保存成 dump.rdb。恢复时直接读这个文件,速度快,文件体积也小。

RDB 的触发方式

RDB 快照有三种触发途径:

第一种是自动触发。由 redis.conf 里的 save 指令控制,比如默认配置:

save 900 1
save 300 10
save 60 10000

这三行的意思是:900 秒内至少有 1 个键发生变化,或者 300 秒内至少有 10 个键变化,或者 60 秒内至少有 10000 个键变化,就触发一次快照。注意这里是"或"的关系,任意一条满足就会触发。

第二种是手动触发。SAVE 命令会阻塞整个 Redis 主线程,直到快照完成,期间所有请求都会卡住,线上绝对不要用。BGSAVE 则会 fork 出一个子进程来做快照,主线程继续处理请求,这是我们该用的方式。

第三种是主从复制和关机时。从节点首次同步主节点数据时,主节点会执行一次 BGSAVE;执行 SHUTDOWN 时,如果没有开启 AOF,也会自动保存一次 RDB。

BGSAVE 的 fork 与写时复制

这里有个绕不开的知识点:BGSAVE 靠的是操作系统的 fork() 调用。fork 出来的子进程和父进程共享同一份内存页,只有当父子进程其中一方要修改某个内存页时,操作系统才会复制一份出来,这就是 Copy-On-Write(写时复制)。

这带来两个实际影响。第一,快照期间如果写入量很大,被修改的内存页会不断被复制,内存占用可能接近翻倍,低配 VPS 上要特别小心。第二,fork 本身是耗时的,内存越大耗时越长,会造成主线程的短暂卡顿。这就是为什么 32GB 内存的 Redis 实例上跑 BGSAVE 经常看到几百毫秒的延迟毛刺。

AOF 追加日志:把每一条写命令都记下来

AOF(Append Only File)的思路完全不同:它不拍快照,而是把每一条会改变数据的写命令,追加到 appendonly.aof 文件末尾。恢复时,Redis 重新执行这个文件里的所有命令,就能还原出数据。因为记录的是"过程",所以 AOF 的实时性天然比 RDB 好。

AOF 的三个核心配置

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

其中 appendfsync 是最关键的取舍,它有三个取值:

always:每条写命令都立刻调用 fsync 刷盘。最安全,理论上最多丢一条命令,但磁盘 IO 压力极大,性能最差,几乎没人这么用。

everysec:每秒刷盘一次。这是默认值,也是性能和安全的最佳平衡点。最坏情况下丢失最近 1 秒的写入。生产环境首选。

no:完全交给操作系统决定什么时候刷盘。性能最好,但可能丢几十秒的数据,只适合纯粹的缓存场景。

AOF 重写:解决文件无限膨胀

AOF 有个天然缺陷:同一个键被反复修改,日志里会堆积一大堆无关的中间命令。比如你对一个计数器执行了 100 万次 INCR,AOF 里就有 100 万条命令,但实际有用的只是最终值。

Redis 用 AOF 重写(rewrite)来解决这个问题。重写时,Redis 遍历当前内存中的数据,用最少的命令重新生成一份 AOF 文件。触发方式同样有自动和手动两种:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

这表示:当前 AOF 文件比上次重写后增长超过 100%,并且体积超过 64MB 时,自动触发重写。BGREWRITEAOF 命令可以手动触发。

混合持久化:鱼和熊掌可以兼得

Redis 4.0 引入了混合持久化,它把 RDB 和 AOF 的优点结合起来:重写 AOF 时,不再生成纯命令日志,而是先生成一份 RDB 格式的全量数据,再追加重写开始后的增量命令。这样得到的 AOF 文件,前半段是 RDB,后半段是 AOF。

恢复时,Redis 先加载 RDB 部分(快),再重放 AOF 增量部分(全)。开启方式非常简单:

aof-use-rdb-preamble yes

这是 Redis 4.0 之后的默认值,建议保持开启。它同时解决了 RDB 恢复慢不了、AOF 恢复慢和体积大的问题,是目前最推荐的组合。

那次故障后,我最终改成的配置

结合我自己的场景(一台 4GB 内存的 VPS,Redis 里既有缓存也有登录会话和少量排行榜数据),最终的配置如下:

# 开启 AOF
appendonly yes
appendfilename "appendonly.aof"
# 每秒刷盘,最多丢 1 秒
appendfsync everysec
# AOF 重写时不要在 fsync 上打架,避免阻塞
no-appendfsync-on-rewrite no
# 重写触发条件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 混合持久化
aof-use-rdb-preamble yes

# RDB 仍然保留,作为兜底和备份的便捷手段
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes

这里有一个容易被忽略的点:no-appendfsync-on-rewrite。当它是 yes 时,AOF 重写期间会暂停 fsync,避免重写产生的磁盘 IO 和刷盘抢夺资源,但代价是这段时间的数据可能丢失。我选 no,是宁可偶尔卡一下也不要丢数据的取舍,你可以根据自己的场景调整。

一个真实的数据安全提醒

配置改完,不代表万事大吉。持久化文件默认放在 Redis 的工作目录,很多人重启服务器后才发现 dump.rdb 被放在了一个临时目录里,或者因为权限问题根本没写成功。请务必在 redis.conf 里显式指定:

dir /var/lib/redis

并且确认这个目录属于 redis 用户、磁盘没有满。那次事故后,我做的第一件事就是写了一个每天凌晨的定时任务,把 appendonly.aof 和 dump.rdb 一起打包备份到另一台机器上——因为持久化防的是进程崩溃,防不了磁盘损坏和误删。

怎么验证你的持久化真的生效了

光看配置文件不算数,要动手验证。登录 Redis CLI:

redis-cli INFO persistence

重点看这几个字段:aof_enabled:1 表示 AOF 已开;aof_last_bgrewrite_status:ok 表示上次重写成功;rdb_last_bgsave_status:ok 表示上次 RDB 保存成功;rdb_last_save_time 是上次成功保存的时间戳。如果这几个状态不是 ok,说明你在裸奔。

另一个实操验证:写入一个测试键,然后重启 Redis,看它还在不在:

redis-cli SET persist_test "hello"
redis-cli SHUTDOWN NOSAVE
# 等进程退出后重新启动
redis-cli GET persist_test

如果返回 hello,说明 AOF 生效;如果返回 nil,赶紧去检查 appendonly 是不是没开、或者 dir 目录写不进去。

小结

Redis 持久化没有"正确答案",只有"匹配场景的选择"。纯缓存、数据丢了能重新生成,那 appendfsync no 甚至只留 RDB 都行;一旦 Redis 里存了会话、计数器、排行榜这类不能凭空重建的数据,就必须开 AOF,everysec 起步,配合混合持久化,并且定期把持久化文件备份到异地。别让一次意外的重启,把你三天的数据悄悄带走。

Last modification:October 5th, 2026 at 08:23 pm

Leave a Comment