Redis之所以快,除了基于内存操作,还因为它有一套精密的持久化机制来防止数据丢失。很多人在配置Redis时,经常在RDB和AOF之间犹豫不决,甚至搞不清楚两者到底有什么区别,以及在生产环境中该如何抉择。问题的本质不在于哪个更好,而在于你的业务场景到底能容忍多少数据丢失,以及你对恢复速度的要求有多高。
RDB持久化是一种快照式存储。你可以把它理解为给Redis内存数据拍一张照片,在某个时间点把内存中的所有数据完整地写入磁盘。触发RDB的方式有三种:手动执行SAVE或BGSAVE命令,或者在配置文件中设置save参数,让Redis在满足特定条件时自动触发。SAVE命令会阻塞主进程,生产环境绝对不要用。BGSAVE会fork一个子进程,由子进程负责将数据写入临时文件,写入成功后替换旧的RDB文件,主进程继续处理客户端请求,这是标准做法。
RDB的底层工作原理当Redis执行BGSAVE时,它利用操作系统的写时复制机制。fork出的子进程与父进程共享同一块内存空间。只有当父进程需要修改某个内存页时,操作系统才会复制该页供父进程修改,子进程看到的内存数据始终是fork那一刻的快照。这就是为什么RDB能保证数据一致性,却不需要锁住整个数据库的原因。RDB文件是一个经过压缩的二进制文件,结构紧凑,非常适合做冷备份。你可以轻松地把几百MB的RDB文件复制到其他服务器上做数据恢复,或者做异地容灾。
RDB的优缺点非常鲜明优点是恢复速度快,直接将文件加载进内存即可,不需要逐条重放命令。文件体积小,适合传输和归档。对主进程性能影响小,因为持久化工作完全交给子进程。缺点是两次快照之间的数据会丢失。如果你配置的是save 60 10000,即60秒内有1万次写入就触发快照,那么在最坏情况下,你可能丢失59秒的数据。对于金融交易或实时统计类业务,这种数据丢失是不可接受的。另外,当Redis数据量很大时,fork操作本身会消耗大量CPU资源,可能导致短暂的请求延迟。
AOF持久化是另一种完全不同的思路AOF记录的是写操作日志,而不是数据本身。每一条修改Redis数据的命令,都会被追加写入AOF文件。你可以把AOF文件理解为一个记录所有写操作的文本文件,Redis重启时,通过逐条执行AOF文件中的命令来重建数据库。AOF有三种写入策略:always、everysec和no。always表示每条写命令都立即同步到磁盘,数据最安全但性能最差。no表示由操作系统决定何时将缓冲区数据写入磁盘,性能最好但可能丢失较多数据。everysec是折中方案,每秒同步一次,最多丢失1秒的数据,这是生产环境最常用的配置。
AOF文件重写机制随着时间推移,AOF文件会越来越大。比如你对同一个key执行了100次INCR操作,AOF文件中会记录100条命令,但实际上最终状态只需要1条SET命令就能表达。AOF重写就是解决这个问题的。Redis通过BGREWRITEAOF命令触发重写,同样fork一个子进程,子进程根据当前内存数据生成最简化的命令集合,写入新的AOF文件。重写过程中,主进程继续处理请求,新的写命令会同时写入旧AOF文件和一个重写缓冲区。子进程完成重写后,将重写缓冲区中的命令追加到新AOF文件末尾,然后原子性地替换旧文件。整个过程不会阻塞主进程,也不会丢失数据。
AOF的优缺点AOF的最大优势是数据安全性高。如果配置everysec策略,最多只丢失1秒的数据。AOF文件是一个纯文本文件,可读性强,你可以直接打开查看每一条命令,方便审计和问题排查。即使文件出现损坏,也可以用redis-check-aof工具修复。缺点是AOF文件通常比RDB文件大很多,恢复速度慢,因为需要逐条执行命令重建数据库。在高写入负载下,AOF的持续写入会对磁盘IO造成压力,可能影响整体性能。不过现代服务器普遍使用SSD,这个影响已经大大降低。
混合持久化:鱼与熊掌兼得Redis 4.0引入了混合持久化模式。它的原理是在AOF重写时,子进程不再将内存数据转换为AOF文本命令,而是直接将内存数据以RDB格式写入AOF文件开头,后续的增量写命令再以AOF格式追加。这样得到的AOF文件前半部分是RDB二进制数据,后半部分是AOF文本命令。重启时,Redis先加载RDB部分快速恢复大部分数据,再重放AOF部分的增量命令。这种方式兼顾了RDB的快速恢复和AOF的数据安全性,是目前生产环境的最佳实践。开启方式很简单,在配置文件中设置aof-use-rdb-preamble yes即可。
生产环境的配置策略如果你的业务对数据丢失极度敏感,比如订单系统、账户余额系统,建议同时开启RDB和AOF,并将AOF的fsync策略设置为everysec。这样即使AOF文件损坏,你还有RDB文件兜底。如果业务允许分钟级的数据丢失,比如缓存预热数据、会话信息,可以只开启RDB,配置合适的save参数,比如save 900 1,即15分钟内至少1次修改就触发快照。对于纯缓存场景,数据完全可以从数据库重建,甚至可以完全关闭持久化,以获得最大性能。
在实际部署中,RDB和AOF的存储路径要配置到不同的物理磁盘上,避免单点故障。RDB文件建议定期备份到远程存储,AOF文件如果太大,可以适当调低auto-aof-rewrite-percentage和auto-aof-rewrite-min-size参数,让重写更频繁一些,控制文件大小。监控方面,要关注bgsave和bgrewriteaof的执行时间,如果fork耗时过长,说明Redis内存过大,需要考虑横向拆分或升级硬件。
常见故障处理AOF文件损坏的情况虽然少见,但一旦发生,Redis会拒绝启动。此时可以用redis-check-aof --fix修复文件,它会截断到最后一个合法的命令处,代价是丢失损坏部分的数据。如果RDB文件损坏,redis-check-rdb工具可以检测文件完整性,但修复能力有限,最好的办法是从备份恢复。还有一种极端情况是服务器突然断电,AOF文件可能只写了一半。Redis在启动时会检测AOF文件末尾是否完整,如果不完整会自动截断,保证能正常启动。
性能调优的细节RDB的压缩参数rdbcompression默认开启,它会使用LZF算法压缩字符串,虽然节省磁盘空间,但会增加CPU消耗。如果CPU资源紧张,可以考虑关闭压缩。AOF的appendfsync设置为everysec时,Redis会启动一个后台线程负责同步磁盘,主线程只负责写入缓冲区,性能影响很小。但如果你发现Redis的延迟突然增大,检查一下是不是AOF重写触发了大量磁盘IO。可以通过设置no-appendfsync-on-rewrite yes,在重写期间暂停fsync操作,把数据安全交给操作系统,以此换取性能稳定。
从数据恢复的角度看,RDB的恢复速度通常是AOF的几倍甚至十几倍。一个10GB的RDB文件加载到内存可能只需要几分钟,而同样数据量的AOF文件重放可能需要几十分钟。这就是为什么混合持久化如此重要。如果你的Redis实例数据量超过50GB,建议认真评估一下纯AOF的恢复时间是否满足你的SLA要求。
选型决策框架总结一下,选型时问自己三个问题。第一,你能容忍多少数据丢失?如果答案是零,那就必须用AOF,并且fsync设为always或everysec。第二,你对恢复速度的要求有多高?如果需要分钟级恢复,RDB或混合持久化是必选项。第三,你的磁盘IO预算有多少?如果磁盘性能有限,优先考虑RDB,减少持续写入压力。大多数互联网业务的最佳实践是混合持久化加everysec策略,既保证了数据安全性,又获得了不错的恢复速度。
理解RDB和AOF的本质区别后,你会发现它们并不是互斥的,而是互补的。RDB负责快速恢复和大规模备份,AOF负责数据安全和不丢数据。把它们结合起来,再配合合理的配置和监控,就能构建一套可靠的Redis持久化方案。真正重要的是根据业务特点做出权衡,而不是盲目追求某一种技术。
