聊到 Redis,很多人第一反应是“缓存嘛,快就完事了”。但真在线上跑过 Redis 的人都知道,快只是它的一面,持久化策略才是那个“看不见但真要命”的设计。Redis 的数据都在内存里,一旦进程退出、机器断电、容器被杀,所有写操作的结果都会瞬间蒸发。没有可靠的持久化方案,只敢拿它当纯缓存,出了事故也只能干瞪眼。
这篇文章就围绕 Redis 持久化策略展开,把 RDB 快照、AOF 日志、混合持久化这些方案从原理到配置、从选型到排障过一遍。重点是讲清楚每个配置项背后的为什么,以及我在实际生产和恢复演练中踩过的坑。适合刚接触 Redis 的开发者,也适合已经上了 Redis 但想把持久化配置得更稳的运维同学。
1. 持久化为什么是 Redis 的命门
1.1 内存里的数据,断电就是灭门
Redis 的单机性能几万甚至十几万 QPS,靠的就是把数据全放进内存,读写不走磁盘。可内存本身是个易失存储,断电、进程崩溃、OOM 被杀,数据说没就没。很多人觉得“Redis 崩了重启就行”,这句话只对纯缓存场景成立,冷数据还能从数据库补回来;但如果 Redis 里存的是登录会话、库存计数、排行榜、分布式锁标记,那丢数据就不是缓存击穿的问题,而是业务事故。
我见过一个真实案例:活动服务为了抗住瞬时流量,把库存扣减直接写在 Redis 里,数据库只做异步对账。结果半夜运维重启机器,Redis 没开持久化,重启后库存直接变成初始值,用户狂下单,对账对到天亮。后来一查,问题根本不是 Redis 慢,而是压根没配持久化,数据全在内存里裸奔。
所以持久化不是“要不要”的问题,而是“在什么程度上丢数据你接受不了”的问题。Redis 默认配置里其实有 RDB 快照,save 900 1 这样的规则默认开启,但很多人装完 Redis 后喜欢把 save 全注释掉,等于主动放弃了第一道防线。这个习惯要改。
Redis 官方提供两种核心持久化手段:RDB 快照和 AOF 日志,还有从 4.0 开始的混合持久化方案。三者不是互斥的,生产环境完全可以组合使用。接下来我们把每种的原理、触发方式、优缺点逐个讲明白。
1.2 RDB 与 AOF 的「分工」和各自边界
RDB 是“一段时间内的全量快照”,把某个瞬间的内存数据压缩后写进 dump.rdb 文件。它的优点是恢复速度极快,文件紧凑,适合做冷备、灾备;缺点是快照之间有间隔,一旦在两次快照之间宕机,这期间写入的数据就全丢。你可以把它理解成拍全家福,每次拍照时记录当时的完整状态,照片之间的变化是丢失的最长窗口。
AOF 则相反,它不拍快照,而是把每一次写命令追加到日志文件里,像记账小票一样完整记录所有操作。重启时只要把小票从头到尾回放一遍,就能把内存状态“重演”出来。AOF 的丢失窗口可以做到很小,甚至每次写命令都 fsync 到磁盘;代价是文件体积大,启动时回放比加载 RDB 慢。
混合持久化则是在 AOF 文件头部加了一段 RDB 格式的数据,把“全量快照 + 增量命令”合在同一个文件里:重启时先加载 RDB 头做全量恢复,再回放尾部增量命令补上最后的变化。它兼顾了 RDB 的恢复速度和 AOF 的低丢失率,是 4.0 之后生产环境我最推荐的形态。
下文我会按 RDB、AOF、混合持久化的顺序拆开讲,最后给出一套可直接落地的配置和排障经验。不要嫌啰嗦,持久化这种事,配置错了不一定会马上出事,但出事时一定是大事。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. RDB 快照:性能优先的封存方案
2.1 RDB 怎么触发:save 的三种方式
RDB 的触发有三种方式:配置文件里配 save 规则自动触发,手动执行 SAVE 同步生成,或者执行 BGSAVE 后台异步生成。先说自动触发的配置格式,它是很多新手的迷惑点。
conf复制save 900 1
save 300 10
save 60 10000
这三条规则的含义是:900 秒内如果至少有 1 次写操作,就会触发一次快照;300 秒内至少有 10 次写操作,触发一次快照;60 秒内至少有 10000 次写操作,触发一次快照。注意是“或”的关系,只要任意一条满足就触发,不是三条都要满足。每一条规则都可以独立注释掉,比如把 save 900 1 删掉,只留 save 60 10000,那就意味着只在写入很频繁时才会拍快照。
手动触发方面,SAVE 是前台调用,需要 Redis 主进程阻塞直到快照写完。阻塞期间 Redis 无法处理任何命令,线上几乎没人这么干,除非你明确知道此时无所谓延迟。BGSAVE 则是 fork 一个子进程,由子进程负责写文件,父进程继续处理请求,这才是常规操作。我在做备份脚本或变更前准备时,都会用 BGSAVE 触发一次最新快照,再复制 dump.rdb 留档。
注意:如果你把配置文件里的所有 save 规则都注释掉,Redis 就不会自动生成 RDB,即使有写入也不会。很多人写“关闭持久化”就是这么关的。不是说不能关,但你要清楚这是主动放弃了 RDB 这一层保障。
2.2 快照背后的 fork 与 COW 原理
很多人不理解 BGSAVE 为什么“不阻塞”。这背后是 Linux 的 fork 系统调用和写时复制(Copy-On-Write,COW)机制在支撑。
Redis 执行 BGSAVE 时,主进程会 fork 出一个子进程。fork 之后,子进程和父进程共享同一份内存页,也就是说子进程看到的是一份“当时内存的完整副本”。如果父进程之后没有写操作,两个进程的内存完全一致,子进程只需把这份共享内存直接写到 dump.rdb 即可,不需要复制全部数据。
但 Redis 不可能完全不写。父进程一旦有写请求,涉及的内存页会被复制一份出来,父进程写复制出来的新页,子进程继续读旧页,这样快照反映的是 fork 时刻的数据。这种机制就叫写时复制。打个比方:你请摄影师拍全家福,摄影师的相机先占住一个机位,之后客厅里有人走动,被移动过的家具会被临时换成同款复制品,让照片始终是刚拍时的样子。
COW 带来的坑是:fork 之后如果父进程写入非常频繁,内存页会被大量复制,瞬间内存占用可能涨到平时的数倍甚至更多。如果机器本身内存比较紧,加上开启了大 key、高写入,很容触发 OOM 或增加延迟。所以 BGSAVE 虽然不阻塞命令处理,但并不是零成本的“后台任务”。
2.3 用 RDB 必须接受的取舍
RDB 的优点很直观。文件是压缩后的二进制快照,体积小,恢复时直接加载进内存,速度远快于回放 AOF。它特别适合做整机级别的灾备,比如每天凌晨生成一份 dump.rdb,上传到对象存储,万一机房挂了还能拿到最近一天的数据。
RDB 的缺点也很明确:快照间隔内的数据会丢。默认配置下,如果系统在 60 秒内写了 9999 次,还差一次才到 10000 的那条触发线,这时候宕机,全部丢失。如果想缩小丢失窗口,就得把 save 阈值调得越来越密,但频繁 fork 和频繁写盘又会带来性能压力。此外,RDB 文件是 Redis 版本的“私有格式”,跨大版本恢复可能出现不兼容,升级 Redis 时建议先用新版本试加载一遍。
所以我对 RDB 的定位是“恢复主轴 + 冷备主力”。它不是零丢失方案,但它恢复够快、结构够简单。配合 AOF 和混合持久化之后,它的劣势会被补上,优势依然被保留。
3. AOF 日志:数据安全的细水长流
3.1 AOF 写的是“操作过程”,不是结果
AOF 和 RDB 最本质的区别在于:RDB 记录的是某一时刻的内存状态,AOF 记录的是达到当前状态所执行过的所有写命令。比如你执行了 SET key 1、INCR key 三次,RDB 里最终保存的就是 key = 4,而 AOF 里会依次记录四条命令。恢复时 Redis 会把 AOF 里的命令从头到尾重新执行一遍,最终得到相同的数据。
AOF 文件的基本格式就是 Redis 协议文本,直接 cat appendonly.aof 都能看到类似 *3\r\n$3\r\nset\r\n$3\r\nkey\r\n$1\r\n4\r\n 的记录。这不只是为了给人看,而是让 Redis 启动时能复用命令解析器快速回放。因为 AOF 是逐条追加写命令,所以每次都把整个文件大小记录下来、原样恢复,这是它比 RDB 更“细水长流”的原因。
但这里有个容易被忽略的问题:如果 AOF 只追加不整理,文件会无限膨胀。一条 key 被反复修改上千次,AOF 里就会留下上千条操作记录,而真正恢复时只需要最后的最终值。这就引出了 AOF 重写机制。
3.2 AOF 的三种 fsync 策略怎么选
AOF 的写盘分为三步:命令先追加到内存中的 aof_buf;再在每次事件循环结束时写入操作系统的内核缓冲区(page cache);最后由 fsync 强制刷到物理磁盘。真实的落盘时机由 appendfsync 配置控制,它有三个选择:always、everysec、no。
| appendfsync 配置 | 写盘时机 | 性能影响 | 断电可能丢失的数据 |
|---|---|---|---|
| always | 每条写命令同步 fsync | 最慢,吞吐量明显下降 | 最多丢一条命令,接近零丢失 |
| everysec | 每秒钟批量 fsync | 性能接近 no,但磁盘吃力时会出现短暂延迟 | 最多丢 1 秒的写命令 |
| no | 由操作系统决定何时刷盘 | 最快,但刷盘时机不可控 | 可能丢最后一次刷盘前的较多数据 |
生产环境我几乎固定用 everysec。它在绝大多数情况下能把丢失窗口控制在 1 秒以内,性能代价又小。always 适合对账、财务这类哪怕丢一条命令都接受不了的业务,但你要做好吞吐量打折的准备。no 我基本不推荐,因为它把决定权交给了操作系统,Redis 本身完全失控,跟“关了持久化”在某些场景下区别不大。
这里必须强调:everysec 不是“最多丢 1 秒数据”的绝对保证。如果系统突然断电,内核缓冲区还没来得及刷盘的那部分命令会丢;如果 Redis 进程只是异常退出但机器没断电,Redis 还能在退出前尽量把 aof_buf 刷到磁盘,丢失情况会好很多。别把 everysec 吹成零丢失,心里要有这个预期。
3.3 AOF 重写:日志变大的自救机制
AOF 文件越滚越大,启动恢复就会越来越慢,磁盘占用也越来越夸张。所以 Redis 提供了重写机制,本质是“用当前内存里的数据生成一条最小命令集,替代掉历史 AOF 文件里的全部命令”。比如某个 key 被 set 了一千次,重写后只保留最后一次的 set 命令。
重写有两种触发方式:手动执行 BGREWRITEAOF,或者按配置自动触发。自动触发依赖两个参数。
conf复制auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
含义是:当前 AOF 文件大小比上次重写后的大小增长了 100%(也就是翻了一倍)时,并且文件大小超过 64 MB,才会触发重写。第一个参数控制“增长速度”,第二个参数控制“绝对下限”,避免 Redis 刚启动时频繁重写小文件。
重写过程依然依赖 fork 子进程,而且重写期间父进程继续写入的命令会先放到重写缓冲区里。子进程生成完新的 AOF 文件后,Redis 会把缓冲区里的增量命令追加到文件尾部,最后替换旧文件。如果写入量大,重写期间的内存和磁盘压力都不小,线上要留意 auto-aof-rewrite-percentage 不要调得太激进,否则重写会变成频繁的隐形负担。
4. 混合持久化:生产环境的主流答案
4.1 混合模式怎么解决“既要又要”
RDB 恢复快但丢数据多,AOF 丢数据少但文件大、恢复慢。能否取两者之长?Redis 4.0 给出了答案:开启 aof-use-rdb-preamble yes 之后,AOF 文件在重写时会生成一个“RDB 头 + 增量 AOF 尾”的混合文件。
这个混合文件的样子是:文件前段是 RDB 格式的二进制快照,相当于记录了重写那一刻的完整数据;后段是重写之后产生的增量写命令,以普通 AOF 格式追加。启动加载时,Redis 先加载前段的 RDB 快照,再把后段的增量命令回放进去。相比纯 AOF,它不需要把成千上万条重复命令从头执行一遍,恢复时间大幅缩短;相比纯 RDB,它又在快照基础上多追加了最近一段时间的写操作,丢失窗口进一步缩小。
需要注意,混合持久化是“重写后生效”,不是说配置一改,现有的 AOF 文件立刻变成混合格式。重启 Redis 后,需要手动执行一次 BGREWRITEAOF,或者等待自动重写触发,新的 AOF 文件才会带上 RDB 头。线上从纯 AOF 迁移到混合模式时,我会主动先触发一次重写,避免重启后加载的还是旧逻辑。
4.2 开启混合持久化后,原来的配置还要不要管
很多同学以为开了混合模式,RDB 的 save 配置就没用了。这是误会。aof-use-rdb-preamble 只影响 AOF 重写后生成的文件格式,RDB 作为一种独立的持久化机制仍然照常运行,默认的 dump.rdb 备份逻辑依然存在。我通常保留 save 规则,这样即使 AOF 文件损坏,还有一份相对完整的 RDB 可以做兜底恢复。
混合模式下,备份 AOF 文件时要注意:文件前半部分是 RDB 二进制,后半部分是文本协议,不能再用普通文本编辑器去改它,也别指望通过 grep 搜索得到所有 key。直接用 redis-check-aof 做健康检查,或者整体复制文件留档即可。
另外,混合持久化对 Redis 版本有要求。4.0 及以上版本默认支持,4.0 之前的版本加载不了带 RDB 头的混合 AOF 文件,降级重启会直接报错。如果你有跨版本迁移的需求,先确认目标版本支持,不要盲目拿新版生成的混合文件去老版本启动。
5. 持久化策略的选型与场景化配置
5.1 选型对照表:什么时候用哪种
没有绝对最好的持久化策略,只有最匹配业务容忍度的选型。下面是我自己的经验对照表,不是官方文档,但很实用。
| 业务场景 | 推荐策略 | 评估口径 |
|---|---|---|
| 纯缓存,可接受冷数据回源 | 仅 RDB,或甚至关闭持久化 | 丢了可以从数据库重建,重点是降低性能损失 |
| 会话、验证码、短期临时数据 | RDB + AOF everysec | 数据量小、丢失容忍度低,AOF 兜底更稳 |
| 库存、计数、分布式锁标记 | RDB + 混合模式 | 丢失窗口尽量小,恢复又要快,混合最合适 |
| 数据总量大、恢复时间有 SLA | RDB + 混合模式,且保留独立 RDB 备份 | 恢复主路径走 RDB 头,增量回放控制在最小范围 |
| 对账、财务流水类强一致需求 | AOF everysec + 始终开启 always 模式 | 这里我建议 always,丢命令不可接受 |
还需要考虑部署形态。单机 Redis 和主从集群的持久化策略选择逻辑不太一样:主从架构下可以在主从复制层做冗余,但持久化最好还是主节点或从节点至少一方完整开启,不能两头都不管。
5.2 一套生产可用的配置模板
直接给一套我常用的 Redis 持久化配置片段,基于 Redis 6.x / 7.x 验证过,同样适用于 4.0 以上版本。
conf复制# AOF 基础配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
# AOF 自动重写控制
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
dbfilename dump.rdb
逐个说明关键参数的用意。no-appendfsync-on-rewrite 默认是 no,意味着 AOF 重写期间依然会正常 fsync,这样更稳,但磁盘压力会上升;如果磁盘写入能力吃紧,可以考虑临时调成 yes。stop-writes-on-bgsave-error 建议保持 yes,当 RDB 快照写盘失败时 Redis 会主动拒绝写命令,避免脏数据继续膨胀后连补救的机会都没有。rdbchecksum 保留开启,redis-check-rdb 校验时会更快发现问题。
在部署时,还要给 Redis 数据目录预留足够空间。AOF 重写会临时生成新文件,RDB 快照也要求目录可写。我之前吃过一次亏,数据盘满了,Redis 一直尝试重写失败,换成 stop-writes-on-bgsave-error yes 后直接拒绝写入,把业务挡住了,排查起来比数据静默丢失好办得多。
6. 实操过程与核心环节实现
6.1 查看持久化状态:INFO persistence 手把手
配置配没配好不能靠感觉,必须用命令验证。Redis 的 INFO persistence 段会输出当前持久化的实时状态,重点关注这些字段。
bash复制redis-cli INFO persistence
输出的关键字段:
text复制loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1690000000
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:1
rdb_current_bgsave_time_sec:-1
aof_enabled:1
aof_rewrite_in_progress:0
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:33554432
rdb_last_bgsave_status 为 ok,说明最近一次 BGSAVE 成功;aof_last_write_status 为 ok,说明最近一次 AOF 写入没有报错。rdb_changes_since_last_save 表示距离上次 RDB 快照以来发生了多少次写操作,这个值和 save 规则配合,可以推断是否快要触发了。aof_last_cow_size 表示最近一次 fork 重写时的 COW 内存消耗,如果值特别大,说明当时内存页复制量很猛。
查看运行中的配置还可以用:
bash复制redis-cli CONFIG GET save
redis-cli CONFIG GET appendfsync
redis-cli CONFIG GET aof-use-rdb-preamble
这些命令能快速确认运行时配置和配置文件是否一致,排查可疑配置时很顺手。
6.2 手动触发备份与恢复演练
持久化策略必须做过一次完整的“断电复活”演练才算数。我每次搭建完 Redis 服务,都会手动做一遍备份和恢复。步骤不复杂,但很能说明问题。
先用 BGSAVE 生成一份 RDB,再把 AOF 文件也复制到安全位置:
bash复制redis-cli BGSAVE
cp /var/lib/redis/dump.rdb /backup/dump-$(date +%F).rdb
cp /var/lib/redis/appendonly.aof /backup/appendonly-$(date +%F).aof
接着模拟数据丢失场景。为了不干扰真实数据,我会把恢复目录指向一个空的 Redis 数据目录:
bash复制redis-cli SHUTDOWN NOSAVE
cp /backup/dump-$(date +%F).rdb /var/lib/redis/dump.rdb
cp /backup/appendonly-$(date +%F).aof /var/lib/redis/appendonly.aof
redis-server /etc/redis.conf
redis-cli DBSIZE
恢复后重点看 DBSIZE 是否和备份时一致。因为 RDB 和 AOF 我都放了,Redis 启动时会优先加载 AOF(如果 appendonly yes),所以 AOF 文件是否完整直接决定恢复结果。如果只想要 RDB 恢复,可以在启动时临时把 appendonly 关闭,或者暂时移走 AOF 文件,但这属于特殊场景,不要在没弄清楚的情况下对着生产环境乱试。
6.3 常见配置错误:改配置不重启不生效
很多同学直接在 redis.conf 里改参数,然后 redis-server redis.conf 启动,没问题。但如果是已经运行的 Redis,光改配置文件不会自动生效,得用 CONFIG SET 动态修改,或者重启进程。
我的习惯是先用 CONFIG SET 做动态调整,验证没问题后再写到配置文件持久化。比如临时把 appendfsync 改为 always:
bash复制redis-cli CONFIG SET appendfsync always
redis-cli CONFIG REWRITE
CONFIG REWRITE 会把当前运行配置回写到配置文件,这样不用手动编辑文件也能保持重启后生效。但要注意,CONFIG REWRITE 只对支持动态配置的参数有效,有些参数还是得改配置文件重启,这一点要认准官方文档。
另一个新手经常踩的坑是 save 参数相互覆盖。如果你用 CONFIG SET save "60 1000",它会把之前所有 save 规则都替换掉,而不是追加。需要一次设置多条规则时,要用空格分隔:CONFIG SET save "900 1 300 10 60 10000"。这是我见过最多的输出和预期不符的场景之一。
7. 常见问题与排查技巧实录
7.1 fork 阻塞与频繁快照的排查
现象是 Redis 延迟偶尔飚高,命令执行出现明显卡顿,但 CPU 和内存看着又不高。这时优先查看 INFO stats 里的 latest_fork_usec 字段,它记录了最近一次 fork 操作耗时,单位微秒。如果这个值达到几十万甚至上百万,说明 fork 本身阻塞了主线程。
fork 慢的根源通常是内存太大,或者 fork 瞬间系统内存分配器开销高。大 key 会加剧 COW,因为父进程每次写都会复制内存页。先用 redis-cli --bigkeys 找出大 key,能拆分尽量拆分;再检查 save 规则和自动 AOF 重写间隔是否过密。比如你叠了好几层保存规则,一个高写入实例每隔几分钟就 fork 一次,自然会把可用内存吃紧、把系统拖慢。这种情况下适当放宽 save 阈值,保留更粗粒度的快照,反而更健康。
7.2 AOF 文件损坏与 redis-check-aof 修复
Redis 突然断电或者磁盘故障后,AOF 文件尾部可能出现不完整的命令记录,重启时 Redis 会报错拒绝加载。此时安全第一,先把损坏的 AOF 文件复制一份,再做修复。
bash复制cp appendonly.aof appendonly.aof.bak
redis-check-aof --fix appendonly.aof
redis-check-aof --fix 会扫描文件,发现不完整或格式错误的命令时,会问你是否截断到最后一个完整命令之前。选 yes 后,文件被修复到可加载状态。修复结果是会丢失尾部一些未完成写入的数据,这是没办法的事,但总比整个文件不能加载要好。如果同时有 RDB 快照,也可以考虑直接放弃 AOF,用 RDB 恢复后再重建 AOF,哪种方案数据更完整,取决于上次快照的时效和损坏的文件大小。
RDB 文件损坏时,用 redis-check-rdb dump.rdb 检查。这个工具能校验文件结构,但 RDB 不像 AOF 那样容易做局部截断修复,遇到损坏基本只能靠备份恢复。所以我一直强调,RDB 和 AOF 要同时保留,互为兜底。
7.3 主从复制下持久化到底该开在哪
主从部署时,有些团队为了让主节点性能最大化,把主节点的持久化完全关掉,只让从节点开持久化。这种方式理论可行,但如果主节点宕机后执行故障切换,从节点晋升为主节点,数据还在;如果主节点还没宕,只是因为网络分区丢失了从节点,主节点的数据是全新的,一旦主节点发生内存崩溃,还没有来得及同步到从节点的写命令就会丢。
我的建议是主节点和从节点都开启持久化,至少保持 AOF everysec。如果真的追求极限性能,可以关掉主节点的 RDB 快照,但从节点必须开启完整持久化。还有一个细节:全量同步时从节点会清空自身数据,加载主节点传来的 RDB 文件,所以从节点持久化配置合理的话,恢复起来是很快的,不用太担心主从架构下持久化重叠带来的性能问题。
7.4 现场踩坑清单
最后整理一份我在实际维护中反复遇见的坑,每条都有教训。
- save 规则不要设太密,尤其线上写入量大的实例,频繁 fork 的快照会带来不可控延迟。
- appendfsync everysec 不是零丢失,极端断电场景还是会丢秒级数据,重要业务该上 always 就上。
- 开启 aof-use-rdb-preamble 后,旧的纯文本 AOF 工具可能无法正确解析,备份文件前先用 redis-check-aof 验证。
- 手动 BGSAVE 放在业务低峰做,别在促销、秒杀等写入高峰触发。
- RDB 和 AOF 的备份文件要区分目录保留,不能全堆在同一个数据盘,磁盘满了会连锁出问题。
- 升级 Redis 大版本前,先拿旧备份文件在新版本上做一次 load 测试,RDB 格式跨版本不兼容是真实存在的。
- CONFIG SET 修改 save 或 appendfsync 时,记得用 CONFIG REWRITE 固化,否则重启后配置就回滚了。
- 不要在持久化未验证的环境里直接跑生产业务,至少做一次完整的备份恢复演练,把 RDB、AOF、混合模式都试一遍。
