Redis的持久化不是一个非黑即白的选择题。很多开发者一开始会纠结“到底用RDB还是AOF”,但真正在生产环境中跑过大规模缓存或数据库场景的人都知道,问题根本不在于选哪个,而在于你的业务能容忍丢失多少数据、能承受多大的写入延迟、以及恢复数据时能等多久。这三个问题的答案直接决定了你的持久化策略组合。RDB和AOF不是替代关系,而是互补关系,绝大多数高可用场景下,两者是同时开启的。
RDB的核心机制与性能特征RDB本质上是Redis数据的全量内存快照。触发方式有两种:手动执行SAVE或BGSAVE命令,以及通过配置参数自动触发。SAVE会阻塞主进程,线上环境绝对不能用。BGSAVE会fork一个子进程,利用操作系统的写时复制机制,在子进程中将内存数据写入临时RDB文件,写入完成后原子替换旧文件。这个过程对主进程的影响主要体现在fork瞬间的阻塞,如果Redis实例内存很大,比如超过10GB,fork的耗时可能达到百毫秒级别,对延迟敏感的业务会有明显抖动。
RDB的文件是一个经过压缩的二进制文件,结构紧凑。它的恢复速度极快,因为直接把数据映射回内存即可,不需要逐条重放命令。这对于灾难恢复场景非常关键,比如机房断电后需要尽快恢复服务,RDB几分钟就能加载完几十GB的数据。但RDB的致命缺陷是数据窗口问题。假如你配置的是“900秒内至少1次修改”触发保存,那么在两次快照之间如果发生宕机,这15分钟内的写入就永久丢失了。即使调成5分钟一次,依然存在窗口。对于订单、交易这类关键业务,这种丢失是不可接受的。
AOF的运作原理与刷盘策略AOF记录的是Redis收到的每一条写命令,以追加方式写入文件。它解决的是RDB数据窗口过大的问题。AOF有三种刷盘策略:always、everysec和no。always是每条命令都同步刷盘,数据安全性最高,但性能最差,普通磁盘的QPS会直接降到几千。no是完全交给操作系统控制,风险太大。everysec是每秒刷盘一次,由后台线程执行,主线程不阻塞,这是生产环境的标准配置。即使宕机,最多丢失1秒的数据,对绝大多数业务来说完全可以接受。
AOF文件会随着时间不断膨胀,Redis提供了AOF重写机制来解决这个问题。重写不是分析旧AOF文件,而是基于当前内存数据重新生成一份最小化的命令集合。比如对一个key反复修改了100次,重写后只保留最后一条SET命令。重写同样通过fork子进程完成,期间新的写入会同时记录到旧AOF文件和一个重写缓冲区,重写完成后将缓冲区的命令追加到新文件尾部,然后原子替换。这个过程是异步的,不影响正常服务。
AOF的潜在风险在于重写期间的磁盘IO压力和内存占用。如果写入量很大,重写缓冲区可能迅速膨胀,极端情况下导致内存爆掉。另外,AOF文件恢复数据时需要逐条执行命令,速度远慢于RDB。一个几GB的AOF文件,恢复可能需要十几分钟甚至更久,这期间服务不可用,对SLA是很大的挑战。
混合持久化:Redis 4.0后的最优解Redis 4.0引入了混合持久化模式,这是目前生产环境的最佳实践。它的原理是:在AOF重写时,子进程不再生成纯命令的AOF文件,而是先将当前内存数据以RDB格式写入AOF文件头部,再将重写缓冲区中的增量命令以AOF格式追加到文件尾部。这样最终的文件前半部分是RDB,后半部分是AOF。恢复时先加载RDB部分快速恢复大部分数据,再重放AOF部分恢复近期的增量数据。这种设计同时兼顾了恢复速度和数据完整性,是真正意义上的取长补短。
开启混合持久化只需在配置中设置aof-use-rdb-preamble yes,同时开启RDB和AOF即可。Redis会自动处理格式切换,对客户端完全透明。需要注意的是,混合持久化生成的文件前半部分不是标准RDB文件,不能用单纯的redis-check-rdb工具检查,需要用redis-check-aof工具。另外,如果从旧版本升级,需要确认所有节点的Redis版本都支持混合格式,否则主从同步会出问题。
不同业务场景下的策略选择纯缓存场景,数据可以从数据库重建,完全可以关闭持久化。但要注意,如果缓存数据是经过复杂计算得到的,重建成本很高,那就要当成半持久化数据处理。这种情况下可以只开RDB,配置较长的保存间隔,比如15分钟或30分钟,既能保证重启后大部分数据可用,又不会对性能造成持续影响。
消息队列或计数器场景,用Redis做轻量级MQ或实时计数,数据写入频繁且不能丢失,必须开启AOF,刷盘策略用everysec。如果写入并发很高,单机QPS超过5万,建议同时监控AOF重写期间的CPU和磁盘负载,必要时对AOF文件做分盘存储,避免和其他IO密集型服务争抢磁盘带宽。
分布式锁或会话存储场景,数据量通常不大但要求高可用,适合同时开启RDB和AOF混合持久化。这类场景对恢复速度要求高,因为锁状态或会话状态丢失会导致业务逻辑错误。混合持久化能在几秒到几十秒内恢复服务,对业务影响最小。
金融或交易类场景,对数据一致性要求极高,除了同时开启RDB和AOF并配置everysec外,还需要配合主从复制和哨兵或集群模式,甚至要做异地多活。单机持久化永远无法保证100%不丢数据,因为即使always刷盘,磁盘本身也有缓存,掉电照样丢数据。这类场景要在应用层做幂等和补偿机制,Redis持久化只是最后一道防线。
配置调优与常见陷阱RDB配置中,stop-writes-on-bgsave-error参数默认是yes,意思是BGSAVE失败时Redis会停止接受写入。这个设定很激进,目的是让运维人员立刻发现问题。但在某些自动化运维不完善的团队里,这会导致半夜服务突然不可用。如果你的监控告警足够灵敏,可以考虑设为no,让服务继续运行,同时触发告警人工介入。
AOF配置中,aof-load-truncated默认yes,允许加载末尾可能损坏的AOF文件。如果AOF文件因为磁盘满或突然断电被截断,Redis启动时会丢弃不完整的最后一条命令,而不是拒绝启动。这在多数场景下是合理的,但如果你的业务对数据完整性要求极高,应该设为no,让Redis启动失败,然后人工修复AOF文件。
内存超过20GB的大实例,fork操作的延迟会非常明显。这种情况下要特别关注内核的memory overcommit设置,建议设为1,允许内存过量分配,否则fork可能因为系统内存不足而失败。同时,关闭Linux的透明大页,因为大页在写时复制时会复制整个2MB的页面,导致内存占用急剧膨胀,甚至触发OOM killer。
磁盘性能是另一个容易被忽略的瓶颈。AOF的everysec刷盘是顺序写,普通SATA硬盘的带宽也够用,但重写期间的并发读写会让磁盘IOPS飙升。如果使用云主机的网络存储,IOPS有上限,重写可能导致正常写入的fsync延迟增大,表现为Redis响应时间毛刺。解决方案是让AOF文件写入单独的物理盘或高性能云盘,和RDB文件、日志文件分开。
运维监控与自动化持久化策略的有效性最终要靠监控来保障。必须监控的指标包括:最近一次BGSAVE是否成功、BGSAVE耗时、AOF文件大小、AOF重写是否成功、重写期间的内存峰值、以及磁盘剩余空间。很多事故都是因为磁盘满了才发现AOF文件已经写不进去了,Redis却还在正常运行,直到重启时才发现数据全丢了。
自动化方面,可以编写脚本定期备份RDB和AOF文件到对象存储,保留最近7天的备份。备份时先检查文件完整性,用redis-check-rdb或redis-check-aof工具验证,验证通过再上传。恢复演练也很重要,很多团队配置了持久化但从来没恢复过,真到出事的时候才发现文件损坏或者恢复流程不对,这是典型的运维盲区。
Redis 7.0对AOF做了多项改进,包括多部分AOF机制,将AOF文件拆分为基础文件和增量文件,减少重写频率和IO压力。如果你的业务还在用老版本,升级到7.0能明显改善持久化性能。但升级前要充分测试混合持久化的兼容性,尤其是和旧版本节点混部的主从架构。
持久化策略没有银弹,只有根据业务特点做出的权衡。理解RDB和AOF的内部机制,知道它们在什么情况下会出问题,比死记硬背配置参数重要得多。真正的高手不是选对了策略,而是在出问题时知道怎么快速定位、怎么安全恢复、怎么防止再次发生。
