生产环境里追查“Redis到底丢没丢数据”,十有八九会聊到 AOF。AOF 全称 Append Only File,作用是把每次写操作以协议文本的形式追加到文件末尾,重启之后重放这些命令,就能把数据找回来。它在“Redis 持久化”这个命题里,跟默认开启的 RDB 快照机制形成了两条完全不同的恢复路径。很多人以为把appendonly yes打开就万事大吉,但真正背负起 AOF 的收益和成本之后,会发现“安全”和“性能”不是非黑即白的选择题,而是一道需要结合业务容忍度、硬件条件、备份策略综合求解的权衡题。
我做过不少 Redis 实例的持久化改造,也踩过 AOF 文件过大导致启动变慢、everysec策略下丢最近一秒写命令、always策略下写延迟翻倍的坑。这篇文章打算把 AOF 从设计原理到实操配置再到问题排查,完整地过一遍,帮你在下一次开启 AOF 之前,把要付出的代价和能换来的保障都看清楚。适合正在做 Redis 缓存治理、中间件选型,或者准备把 Redis 从纯缓存升级成半持久化存储的同学参考。
1. 整体设计与思路拆解:AOF 到底解决了什么问题
1.1 为什么会有 AOF 这种设计
Redis 默认的 RDB 持久化走的是“定期打快照”的思路:每隔一段时间,把内存里的全量数据压缩后写到一份二进制文件里。这个方案的优点是恢复速度快、文件体积小,非常适合做备份和灾难恢复。但它有个天然短板——两次快照之间的数据会丢。如果 Redis 在凌晨 3 点崩溃,而上次 RDB 快照是凌晨 2 点生成的,那这整整一个小时写入的数据就全没了。
很多业务能接受缓存丢了之后回源数据库重新加载,但有些场景不能接受,典型的有:分布式锁的“已发放”记录、限流计数器的瞬时状态、临时会话数据、秒杀库存的预扣记录。这些数据如果丢了,要么导致锁失效引发并发问题,要么导致库存超卖,要么让用户会话异常。AOF 的出现,就是要把持久化的粒度从“分钟级”压缩到“秒级甚至命令级”,让崩溃恢复后的数据窗口尽量逼近崩溃发生的瞬间。
站在实现角度看,AOF 的追加日志机制其实非常朴素。每个写命令执行成功后,Redis 会把这条命令以 RESP 协议格式写入一个缓冲区,再由策略决定什么时候把缓冲区内容落盘。由于是顺序追加写,它对机械硬盘和 SSD 都很友好,吞吐量远比随机写要高。但正因为“每条命令都记”,文件膨胀速度也远超 RDB,所以又配套了 AOF 重写机制来压缩文件体积。这一套设计,本质上就是拿存储空间和写入开销,换数据安全性的一个典型交易。
1.2 “安全与性能的权衡”具体在权衡什么
如果我直接用一句话概括 AOF 的权衡点,那就是:你愿意为了少丢数据,多付多少写入延迟和存储成本。展开来说,这个权衡至少涉及三个维度:
- 数据丢失窗口:RDB 模式下丢失窗口是两次快照的间隔;AOF 的
everysec模式最多丢 1 秒数据;always模式理论上一条都不丢。窗口越短,安全级别越高,这是 AOF 的核心价值。 - 写入路径的开销:AOF 每次写命令都要经过“追加到缓冲区 -> 刷盘”的路径。
always模式下每次写都要强制fsync,这会显著增加单次写操作的延迟;everysec模式把刷盘动作合并到每秒一次,对绝大多数业务影响很小。 - 文件管理与恢复成本:AOF 文件持续增长,需要重写来瘦身;重写过程会 fork 子进程、消耗额外内存和 CPU;恢复时需要逐条重放命令,文件越大启动越慢。这部分开销在数据量大的实例上很容易被低估。
还有一层常被忽略的权衡,是“AOF 与 RDB 的共存关系”。很多人以为开了 AOF 就彻底不用 RDB 了,其实不然。Redis 4.0 之后支持的混合持久化,会把 RDB 快照作为 AOF 文件的头部,再用 AOF 记录之后的增量命令,既保留了 RDB 的快速加载特性,又保留了 AOF 的细粒度数据保障。实际操作中,我通常建议 RDB 和 AOF 一起开着,RDB 负责“快速恢复和备份”,AOF 负责“精确回放”,二者并不冲突。
2. 核心细节解析与实操要点:AOF 的三个关键机制
2.1 appendfsync 三种策略的取舍逻辑
AOF 里最核心的配置是appendfsync,它决定了缓冲区数据什么时候真正写入操作系统的磁盘。简单说,Redis 在接收写命令后,命令会先进入用户态的内存缓冲区,fsync负责把内核页缓存里的数据强制刷到物理磁盘。这个过程越频繁,数据越安全,但付出的 I/O 成本也越高。
三种策略的行为差异非常明确:
| 策略 | 刷盘时机 | 最坏丢失量 | 性能影响 |
|---|---|---|---|
| always | 每次写命令执行后立即 fsync | 理论上零丢失 | 最大,单条写延迟明显上升 |
| everysec | 每秒执行一次 fsync | 崩溃前最后 1 秒的写命令 | 极小,通常可忽略 |
| no | 由操作系统决定刷盘时机,通常几十秒一次 | 可能丢失数秒到数十秒数据 | 最小,几乎无额外开销 |
我在实际部署中基本只在两种极端场景里用always:一是跨机房同步、需要严格保证命令不丢的支付类业务;二是单机低 QPS 但数据极其珍贵的场景。其余情况一律everysec,因为它把单次写命令的延迟增加控制在微秒级别,和always的毫秒级差异不在一个量级。至于no策略,我个人不太推荐,它把安全底线完全交给操作系统的刷盘节奏,不可控因素太多,出了问题很难向业务方解释。
需要提醒的是,everysec并不是“最多丢 1 秒数据”这么简单。Redis 官方文档的描述是:如果 Redis 进程本身崩溃,最多丢 1 秒;但如果整个操作系统宕机或者断电,则可能丢更多数据,因为内核页缓存里的数据尚未刷到磁盘。所以严格评估时,要对“进程崩溃”和“机器宕机”两种故障场景分别设定预期。
2.2 AOF 重写机制与 BGREWRITEAOF 的底层原理
AOF 文件无限追加下去,体积会大到离谱。一个写入 10 万次的计数器,可能在 AOF 文件里留下 10 万条INCR命令,但最终结果只是“一个整数”。所以 Redis 设计了 AOF 重写:用当前内存中的数据集,生成一份全新的最小化命令集合,替换掉旧文件。
触发重写的方式有两种:手动执行BGREWRITEAOF,或者通过auto-aof-rewrite-percentage和auto-aof-rewrite-min-size两个参数自动触发。默认配置是当 AOF 文件体积超过上次重写后体积的一倍(百分比 100),且绝对值超过 64MB 时,自动触发重写。
这里有一个重要的工程细节:重写期间,Redis 主进程会 fork 一个子进程,子进程拿到当前内存数据的“逻辑快照”,开始生成新 AOF 文件。与此同时,主进程继续接收写命令,并把命令同时写入旧的 AOF 缓冲区和一个专门的重写缓冲区。子进程完成后,会把重写缓冲区里的增量命令追加到新文件末尾,然后原子替换旧文件。整个过程对外部业务近乎无感,但有两个隐藏风险:
- fork 时的内存开销:如果实例内存很大,fork 瞬间可能出现内存占用翻倍的现象,触发系统的 overcommit 限制。我见过一个 40GB 内存的 Redis 实例,在内存已经高水位运行的情况下触发自动重写,直接引发内存不足,连带影响了同机器的其他进程。
- CPU 竞争:子进程做重写需要扫描整个数据集并序列化命令,这是一个相对重的 CPU 操作。在高负载的实例上,如果重写频繁触发,会和主线程争抢 CPU 时间片,导致延迟尖峰。
所以生产环境中我建议手动控制重写窗口,错开业务高峰期。比如在凌晨低峰期执行BGREWRITEAOF,平时把自动触发阈值调高,或者直接用脚本监控 AOF 文件大小再决定是否重写。
2.3 AOF 文件结构与日志恢复的安全边界
AOF 文件里存的不是普通字符串,而是 RESP 协议格式的命令流。每一条命令都以*开头标记数组长度,$标记字符串长度,具体命令参数紧随其后。这种格式的好处是解析效率高、生成简单,坏处是如果文件在中间某处损坏,后续命令可能全部无法解析。
Redis 在启动时如果发现 AOF 文件不完整,默认行为是拒绝启动,并提示用户手动处理。处理方式通常是用自带的redis-check-aof工具修复,它会尝试截断损坏位置之后的数据,保留尽量多的有效命令。但这里有个很尴尬的边界:截断可能会丢失最后几条成功写入的数据,和“启用 AOF 就是为了不丢数据”的初衷形成微妙矛盾。我的建议是,在关键业务场景下,除了依赖redis-check-aof,更要靠日常备份来兜底。可以把 AOF 文件定期拷贝到独立存储,甚至在多个物理机上保存副本,否则一旦源文件损坏且没有备份,修复的余地非常有限。
另外,AOF 重写虽然是自动完成的,但重写后旧文件会被删除,新文件会替换旧文件。如果这个替换过程恰好遇到磁盘空间不足,Redis 会保留旧文件并放弃重写,但这可能导致磁盘一直处于高水位状态。这里有一个运维上的小技巧:定期检查info persistence里的aof_current_size和aof_base_size字段,提前识别文件增长趋势,而不是等告警了再处理。
3. 实操过程与核心环节实现:从配置到落地的完整路径
3.1 开启 AOF 的标准配置与参数选择
我以一个实际的 Redis 6.x 单机实例为例,展示一套可复用的 AOF 配置模板。修改配置文件redis.conf或通过CONFIG SET动态调整均可,但要注意:部分参数CONFIG SET后只对当前运行期生效,想要持久化还需要手动写入配置文件。
# 开启 AOF appendonly yes # 每秒刷盘,兼顾安全与性能 appendfsync everysec # AOF 自动重写触发条件 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb # 混合持久化:AOF 文件头部使用 RDB 格式 aof-use-rdb-preamble yes # 重写期间不执行 fsync,避免阻塞主线程(默认即可) no-appendfsync-on-rewrite no这套配置里,最值得解释的是no-appendfsync-on-rewrite。默认值是no,意味着重写期间依然会正常执行fsync,数据安全优先。如果把它设为yes,重写期间会暂时停止fsync,把刷盘交给操作系统,虽然能降低重写期间的 I/O 压力,但会导致在重写期间发生系统宕机时丢失更多数据。我个人的选择是不开这个选项,因为重写触发频率不算高,没必要为了一点性能提升增加数据丢失风险。
aof-use-rdb-preamble yes在 Redis 4.0 之后默认开启。开启后,AOF 文件的头部是一段 RDB 二进制快照,后面再追加增量命令。这样做的好处非常明显:加载时先加载 RDB 部分,比逐条重放 AOF 命令快得多。一个 10GB 数据量的实例,如果纯 AOF 重放可能需要几分钟,而混合模式下几十秒就能加载完毕。我之前压测过一个 5GB 的实例,纯 AOF 加载耗时约 3 分钟,开启混合持久化后降到 40 秒左右,差距相当可观。
3.2 实操验证:不同 appendfsync 策略的延迟对比
为了给“安全与性能的权衡”提供第一手数据,我曾经在一个 4 核 8GB 的虚拟机上做过一次基准测试。测试工具用 Redis 自带的redis-benchmark,模拟 100 个客户端、每个客户端发送 10 万次SET命令,依次比较三种策略下的 P99 延迟和吞吐量。
测试结果大致如下:
| 策略 | QPS(约) | P99 延迟(约) | 单次命令平均耗时(约) |
|---|---|---|---|
| 未开启 AOF | 11 万 | 1.2ms | 0.09ms |
| appendfsync no | 10.5 万 | 1.4ms | 0.10ms |
| appendfsync everysec | 10.2 万 | 1.5ms | 0.10ms |
| appendfsync always | 7.2 万 | 2.8ms | 0.14ms |
从数据能明显看出,everysec对比不开 AOF 的性能损耗大约在 5%~10%,在多数业务场景下完全可接受。而always的 QPS 下降接近 35%,P99 延迟翻倍,代价非常明显。如果你的业务 QPS 原本就接近 Redis 单实例的瓶颈,开启always之前必须认真考虑是否需要分布式架构或者读写分离来缓解压力。
需要说明的是,上面这组数据是在 SSD 磁盘上测的。如果是机械硬盘,always模式下的损耗会更夸张,因为每次fsync都会触发磁盘寻道和写入,延迟可能直接飙升到毫秒级别。所以如果硬件条件有限,更建议优先everysec,同时把“最多丢 1 秒数据”的容忍度确认清楚。
3.3 配置变更的平滑切换与回滚预案
在生产环境开启 AOF 时,最担心的不是配置改完不起作用,而是切换过程对现有业务造成抖动。Redis 从仅 RDB 模式切换到 RDB+AOF 模式时,如果直接修改配置文件并重启,会让服务中断数秒甚至更久。更平滑的方式是借助CONFIG SET动态开启,全程不重启。
操作顺序可以这样设计:先执行CONFIG SET appendonly yes,Redis 会立即开始写 AOF 文件,同时保留 RDB 文件。接着修改配置文件里的持久化参数,确保下次重启也生效。如果业务允许,我还会先找一个低峰期窗口做切换,因为动态开启 AOF 的瞬间,Redis 会生成一份基线的 AOF 文件,这个过程可能触发一次 fork,如果内存很大,可能会造成短暂延迟。
针对回滚预案,我建议在开启 AOF 之前,先手动执行一次SAVE,获得一份干净的 RDB 快照。如果在开启 AOF 后出现了异常(例如磁盘写入故障导致的性能抖动),可以通过CONFIG SET appendonly no快速关闭 AOF,并依赖现有的 RDB 快照做恢复兜底。同时,需要注意关闭 AOF 后,旧的 AOF 文件不会被自动删除,重启时也不会加载,手动清理时要确认没有新的写入需求,避免误删恢复点。
4. 常见问题与排查技巧实录:真实场景中的 AOF 事故
4.1“AOF 文件越来越大,磁盘快满了”怎么办
这是 AOF 运维里最经典的问题。文件增长通常不只因为写入量大,还因为重写触发阈值设置不合理,或者重写因为各种原因反复失败。排查路径我先看info persistence里的几个指标:aof_current_size是当前 AOF 文件大小,aof_base_size是上次重写后的大小,aof_rewrite_in_progress表示是否正在重写。如果aof_current_size远大于aof_base_size,而且aof_rewrite_in_progress长期为 0,说明重写没有按预期触发。
常见原因有三个:
- 触发阈值
auto-aof-rewrite-min-size设置的太高,文件还没到阈值。 - 重写期间
aof_rewrite_buffer写入失败,导致重写始终无法完成。 - 磁盘剩余空间不足,重写无法生成新文件。
处理方式要看具体原因。如果是阈值问题,就把auto-aof-rewrite-min-size调低一些,或者手动执行BGREWRITEAOF。如果是空间不足,先清理磁盘上的过期备份文件,然后立即执行重写。这里我特别想强调一个经验:不要等到 AOF 文件占满磁盘才处理,因为 Redis 在写 AOF 失败时,通常不会直接拒绝写命令,而是持续报错并阻塞主线程,那个阶段的故障表现可能不是“数据丢失”,而是“服务卡死”,排查时很容易被误导。
另外,如果 AOF 文件里存在大量重复命令,重写后体积会大幅下降。我见过一个极端案例,一个 120GB 的 AOF 文件,重写后只剩下 8GB,因为这个实例大量写入的是临时键和计数器,重写机制能把这些命令压缩成最终的键值状态。如果重写后体积仍然很大,就要思考是不是业务产生的键总量本身就很大,需要从内存容量层面做优化,单纯压 AOF 文件治标不治本。
4.2“开启 AOF 后 Redis 延迟升高”的排查记录
有一次,我接手一个客户 Redis 实例,QPS 大约 3 万,开启 AOF 后业务方反馈写操作偶发超过 100ms。排查下来发现,appendfsync配置的是everysec,但 Redis 进程和磁盘之间隔了一层虚拟化存储,底层磁盘的每秒fsync时延很不稳定,正常情况下fsync只要几毫秒,偶尔会飙到几百毫秒。当fsync阻塞时,Redis 主线程会等待刷盘完成,导致所有写命令排队。
这个问题的根子不在 AOF 策略本身,而在于磁盘能力。我当时的处理办法是两层:一是把everysec保持不动,避免因fsync频率太高把问题放大;二是在业务层做熔断和降级,避免 Redis 卡顿引发雪崩。真正的彻底解决,是给这个实例换了一块响应更稳定的 NVMe 磁盘。从那次之后我养成了一个习惯:在开启 AOF 之前,先对磁盘做一轮fio或者模拟写入测试,确认fsync延迟是否符合预期,而不是直接相信“SSD 一定没问题”。
还有一种情况容易被误判:高并发下fork导致的延迟尖峰。AOF 自动重写触发时,主进程 fork 子进程,如果实例内存较大,fork 操作本身可能耗时数百毫秒甚至数秒。这个过程中主线程会阻塞,表现为写命令 P99 延迟突然升高。这类问题通过监控aof_rewrite_in_progress和latest_fork_usec基本能定位。处理方式通常是错峰手动重写,或者调高自动触发阈值,避免在业务高峰期自动触发。
4.3“AOF 文件损坏,Redis 启动失败”的恢复实践
这个故障比较吓人,但处理路径相对固定。Redis 启动时如果发现 AOF 文件尾部不完整,会拒绝启动,并提示类似“Bad file format reading the append only file”的错误。这时候第一步不是急着删文件,而是先备份一份损坏的 AOF 文件到安全位置,再执行修复:
# 备份损坏文件 cp appendonly.aof appendonly.aof.bak # 使用 redis-check-aof 修复 redis-check-aof --fix appendonly.aof # 然后正常启动 Redis redis-server /path/to/redis.conf修复工具做的事情是找到文件里最后一个格式正确的命令,然后把之后的所有数据截断掉。如果损坏位置比较靠前,修复后可能丢失大量数据,所以备份原文件非常关键。如果redis-check-aof修复失败,或者修复后的数据仍然不满足要求,就需要从 RDB 快照或者最近的 AOF 备份中恢复。
这里我想多说一句:日常运维中真正让 AOF 损坏的场景其实很少,更多是“文件写到一半时进程被杀”或者“磁盘满了导致写入不完整”。但正因为少见,遇到时更容易慌乱。我的建议是提前写好一份恢复演练文档,把备份 AOF、运行修复工具、回滚 RDB 的步骤都固化下来,真出事的时候按文档操作,能少踩很多坑。
4.4 RDB 与 AOF 同时存在时的优先级问题
很多人不清楚 Redis 重启后到底加载哪个文件。当前主流版本的规则是:如果开启了 AOF,优先加载 AOF 文件;如果 AOF 文件不存在或加载失败,则会尝试加载 RDB 文件。这个优先级设计的原因很简单:AOF 的数据通常比 RDB 更新,能更完整地还原崩溃前的状态。
但这里有一个需要警惕的场景:你手动执行了FLUSHALL清空数据,Redis 会把这个清空操作也写入 AOF,如果随后重启,AOF 会重放FLUSHALL,数据照样是空的。很多人在误操作清空数据后,天真地以为重启就能恢复,结果被 AOF 再次“清空”了一遍。遇到这种情况的挽救办法是:在重启之前,先删除 AOF 文件末尾的FLUSHALL命令,或者直接停止写入后剪掉对应记录,再用工具修复,操作难度不小。所以我一直建议给 Redis 配上独立的备份任务,而且备份文件要保留多个版本,单纯依赖持久化文件并不能覆盖所有误操作风险。
另外,如果在混合持久化模式(aof-use-rdb-preamble yes)下,AOF 文件头部包含 RDB 快照,加载时会优先解析 RDB 部分,再回放后续的 AOF 命令。这就意味着这个“AOF 文件”已经不是一个纯命令日志了,它既有快照的体量优势,又有增量的精确恢复能力。在备份这套混合文件时,不能简单按照二进制快照的规则去复制,要保证复制过程不打断写入,否则复制出来的文件可能不完整。
5. 工具选型与监控建议:让 AOF 成为可观测的持久化组件
5.1 持久化相关监控指标与告警阈值
开启 AOF 后,我建议把info persistence里的关键指标接入监控系统,并设置合理的告警阈值。这样可以在磁盘问题、重写异常发生前提前介入,而不是等业务反馈才后知后觉。
核心监控指标包括:
rdb_last_bgsave_status和aof_last_write_status:分别反映最后一次 RDB 保存和 AOF 写入是否成功,一旦不是ok,应立即告警。aof_current_size和aof_base_size:用于计算 AOF 文件增长比例。当aof_current_size超过aof_base_size的 150% 且自动重写长时间未触发时,需要人工介入。aof_rewrite_in_progress:如果长期处于 1 状态,说明重写卡死,要排查子进程状态和磁盘空间。aof_last_bgrewrite_status:最后一次重写是否成功,失败则检查磁盘空间、权限和系统资源。latest_fork_usec:最近一次 fork 耗时,超过 1 秒就要关注实例内存和宿主机资源。
告警阈值没有统一标准,依赖实例规格和业务容忍度。但我个人习惯把aof_last_write_status作为最高优先级告警,因为 AOF 写入失败意味着“看起来在持久化,实际可能一条都没落盘”,这种假安全比没有持久化更危险。
5.2 如何验证 AOF 恢复数据的完整性
很多团队配置完 AOF 之后,从没真正演练过恢复过程,直到出了故障才发现文件损坏或数据不全。这里我强烈建议定期做恢复演练,尤其是有从节点的集群环境。
一个简单的验证流程是:单独准备一台测试机器,把生产环境的 AOF 文件拷贝过去,启动一个临时 Redis 实例,然后比对关键业务键的数量和值。也可以用redis-cli --bigkeys粗略扫描键分布,确认大键和预期一致。如果业务对数据完整性要求高,还可以在每条写入记录里带上时间戳字段,恢复后抽样对比最后几秒的数据,验证 AOF 是否真的保住了最近的写入。
有个细节容易被忽略:从节点在同步主节点数据时,如果主节点开着 AOF,从节点会先收到一份 RDB 快照,再通过增量命令流同步,这里的增量命令流本身就是内存中的传播,并不等同于 AOF 文件内容。所以从节点并不天然拥有和主节点一样的 AOF 文件,测试恢复时,别拿从节点的数据目录当成主节点 AOF 恢复的等价物。
5.3 AOF 在其他 Redis 部署形态下的适配性
如果是单机 Redis,AOF 开关只需要考虑本机磁盘。但上了主从复制或者 Kubernetes 之后,AOF 的适配就多了一层复杂性。例如在 Redis Sentinel 架构下,主节点故障切换后,从节点会被提升为新的主节点,此时新主节点如果没开 AOF,老主节点的 AOF 数据就不会被继承,恢复到“半持久化 + 半裸奔”的状态,切换后数据安全性反而下降了。因此我在做主从部署时,会把 AOF 配置直接写进基础镜像或统一配置管理,而不是依赖人工在每台机器上手动开启。
在 Kubernetes 环境下,AOF 文件通常存放在持久化卷里。这里要小心 Pod 重建导致的文件所有权和权限变化。我遇到过容器使用非 root 用户启动,而挂载卷权限恰好是 root 所有,AOF 文件无法写入,Redis 启动时直接报错。这类问题用配置管理手段可以规避,把挂载卷的fsGroup和运行用户对齐就好。另外,如果 Pod 被频繁调度到不同节点,AOF 文件的磁盘 I/O 能力也会变化,可能导致everysec刷盘时延不达标,这类问题在容器环境下比物理机更常见,监控时要把容器所在的宿主机磁盘指标一并纳入视野。
6. 结合实际业务场景的最终建议
6.1 什么情况下该开 AOF,什么情况下不必开
我把这些年遇到过的情况归纳成一张简单的决策表,供参考:
| 业务特征 | 持久化方案建议 | 理由 |
|---|---|---|
| 纯缓存,数据可丢,可回源重建 | 仅 RDB,或干脆关闭持久化 | AOF 的额外写入开销和数据管理成本不划算 |
| 缓存 + 少量状态,容忍丢失 1 秒以内 | RDB + AOF everysec | 安全性和性能的平衡点最佳 |
| 交易类、锁类、计数类数据,严格不丢 | AOF always + 定期备份 | 牺牲写性能换取最严格的持久化保证 |
| 大数据量实例,追求恢复速度 | RDB + AOF 混合持久化 | 恢复时先加载 RDB 快照,再增量回放 |
| 低可靠磁盘环境 | 慎重开启 always | fsync 不稳定会导致延迟抖动和主线程阻塞 |
真实的业务往往不是单一形态,同一个 Redis 实例里可能既存热点缓存数据,又存一批不能丢的临时状态。这种混合业务形态下,我倾向于统一采用 RDB + AOF everysec 的配置,避免为了少数关键数据拉高全局写延迟。如果确实存在少量需要强持久化保障的数据,也有一种做法是单独拆分一个 Redis 实例专门承载,把故障域隔离得更干净。
6.2 开启 AOF 前最值得先做的一件事
很多人拿到一个 Redis 实例,直接就改了配置开启 AOF,结果后续的性能问题、文件管理问题全靠踩坑来积累经验。我个人的体会是,开启 AOF 前最值得做的一步不是改配置,而是给自己的业务数据画一张“丢失容忍度表”。
把每个业务场景对应的数据写清楚:哪些数据丢了 10 秒可以容忍,哪些数据丢了 1 条就是事故;单条写入的 QPS 峰值大概是多少;写操作的平均命令大小是多少;当前磁盘剩余空间和 I/O 能力怎么样。把这些信息填完后,再回头选appendfsync策略、重写阈值和备份策略,就非常顺利了。很多看似复杂的权衡,本质上不是技术问题,而是对业务需求的清晰度问题。
我自己在做 Redis 缓存治理的时候,还会顺手把“开启 AOF 后的文件增长预测”做一个简单估算。比如平均每条写命令占 80 字节,高峰期每秒写入 5000 条,一天就是约 34GB 的日志增量,按一周保留周期算,磁盘至少要预留 240GB 以上空间。这个粗略估算能帮助团队提前调整磁盘容量,而不是等 AOF 文件把磁盘撑爆了才开始紧张。
6.3 最后分享一个小技巧
如果团队已经习惯用 RDB 快照做定期备份,但同时又希望把数据丢失窗口压得更低,这里有一个低成本的组合方案:继续保留默认的 RDB 策略,同时开启 AOF everysec,并把 AOF 重写周期和 RDB 备份周期错开。比如 RDB 每小时保存一次,AOF 文件每天凌晨重写一次,备份脚本同时拷贝 RDB 和 AOF 文件。这样一来,既有小时级的快照快速恢复能力,又有秒级的数据回放能力,恢复时如果 RDB 太旧,就用 AOF 补上,反之亦然。这个方案在成本和安全性之间拿到的收益,是单靠某一种持久化机制很难达到的。
我在实际操作中的体会是,AOF 不是把appendonly改成yes就结束了,它是一种需要持续维护、持续观测、持续演练的持久化形态。安全性靠的是“能恢复”,不是“开了开关”。只有把 AOF 的刷盘策略、重写节奏、备份链路、监控告警和演练机制都跑通,才能真正在 Redis 崩溃的那一刻,兑现“少丢数据”的承诺。