写Redis做了这么多年几乎每个刚接触它的人都会问同一个问题数据全在内存里断电了怎么办Redis持久化就是为了解决这个问题的。RDB和AOF这两套机制一个是快照、一个是追加日志原理完全不同取舍方向也不一样。网上讲这两个东西的文章一抓一大把但大多数要么就是把官方文档翻译一遍要么只讲个概念对比真正能把这些细节串起来、告诉你生产环境里怎么选怎么配、出了问题怎么排查的内容确实不多。这篇我把自己折腾Redis持久化的经验完整梳理一遍从底层机制到配置调优再到故障排查尽量用大白话讲透给正在做缓存治理、高并发设计、分布式锁这类场景的同学一份可以照做的参考。先说个结论帮大家稳住预期如果你只是把Redis当纯缓存数据丢了可以从数据库重建那开不开持久化都无所谓只要Redis里放了不能随便丢的业务数据我建议默认组合就是开AOF、把appendfsync设成everysec、同时开启混合持久化。这个结论背后是什么逻辑为什么不是无脑选RDB也不是无脑选always刷盘下面展开细聊。1. 为什么Redis需要持久化先搞清楚问题本身1.1 内存的快是Redis最大的优点也是最大的软肋Redis的所有读写操作都在内存里完成所以单实例轻松扛住十万级QPS不在话下这在绝大多数业务场景里已经够用了。但内存有一个天然的短板它是易失性存储断电、进程崩溃、系统重启数据都会没掉。一台装了8GB数据的Redis进程一挂这8GB数据就没了如果这些数据没有从其他渠道恢复的概率那就是事故。很多人最开始学Redis的时候会有一种错觉Redis不是有缓存功能吗缓存丢了重新从数据库查一遍不就行了这话对一半。纯缓存场景确实可以这样但Redis的实际定位早就超出了缓存这两个字。现在大家用它做分布式锁、排行榜、限流计数器、秒杀库存、用户会话、甚至当部分业务的数据库在用。这些场景里的数据一旦丢失损失的不只是缓存命中率而是直接的业务错误或者用户数据丢失。我之前遇到一个团队用Redis存秒杀活动的预扣库存没开持久化一次服务器重启库存数据全没了活动直接全场免费这个教训是非常惨痛的。1.2 持久化到底解决了哪三个问题第一个是宕机恢复。进程崩溃或者机器重启之后Redis能通过持久化文件把自己之前的数据重新加载回来不需要从上游数据库或者日志里慢慢回放。恢复速度直接决定了系统瘫痪时间。第二个是容灾与迁移。做机房迁移、实例扩缩容、故障演练的时候有持久化文件就相当于有了一份数据快照拷贝过去就能在新环境里恢复服务。没有持久化搞迁移的时候只能先切流量再慢慢预热整个过程又慢又揪心。第三个是主从复制的基础。Redis主从全量同步的时候主节点会把内存数据做成RDB快照发给从节点这个动作本身就是持久化机制的一部分。所以即使你的业务完全不怕数据丢只要用了主从复制、哨兵、集群这些高可用架构就绕不开持久化相关的话题。后面我会单独展开讲。所以说持久化不是Redis的一个可有可无的加分项而是从单机工具走向生产级基础设施的关键一步。理解RDB和AOF不是面试前背两个名词解释就完事的问题而是运维、调优、排障时实打实要用到的基本功。2. RDB持久化定时拍一张全量数据的合影2.1 RDB的底层工作方式RDB的全称是Redis DataBase本质上就是给Redis内存里的数据拍一张快照然后以二进制格式写入磁盘生成一个dump.rdb文件。触发方式分两种SAVE和BGSAVE。SAVE是同步的直接在主进程里执行数据写入操作期间Redis完全阻塞不处理任何请求。这种方式只有胆子特别大或者数据量特别小的时候才敢碰正常生产环境下线上用SAVE等于自杀大家千万别试。BGSAVE是异步的Redis会fork出一个子进程来执行快照写入父进程继续处理客户端请求。这是生产环境里实际使用的方案。触发BGSAVE的方式有很多后面详说。子进程写快照的时候父进程还在不断接收写请求这里就涉及RDB最核心的技术点写时复制Copy On WriteCOW。2.2 fork子进程与写时复制的原理COW这个机制值得单独拎出来讲清楚因为很多线上故障都跟对它的理解不到位有关。当BGSAVE触发时Redis主进程会调用fork创建一个子进程。fork这个操作在Linux上的成本不是复制全部内存数据而是复制父进程的页表然后把父子进程指向同一批物理内存页面。创建的这个子进程看到的是一份内存视图这个视图的内容定格在fork发生的那一刻。子进程开始往磁盘上写RDB文件的时候实际上就是在顺序读取这个内存视图将其编码成RDB格式。关键的问题来了fork之后如果父进程没有任何写操作发生那大家都共享同一份物理内存整个过程又轻又快。但如果父进程这时收到了写请求比如一个SET命令那么这块即将被修改的内存页就不能再共享了否则子进程看到的视图也被改了快照就不一致了。所以操作系统会把这页内存复制一份父进程往复制出来的新页上写子进程仍然读原来的旧页。这个机制就是写时复制。用生活化的方式理解RDB快照就像给一家人拍了张全家福按下快门之后就定格了。拍摄期间有人打了个喷嚏、换了件衣服照片里依然是快门那一刻的模样。COW保证的就是快门时刻的一致性代价是如果拍照期间家人不停乱动那需要复制的新照片底片就越多消耗的内存和磁盘IO也就越大。这里就引出一个实操经验Redis实例占用内存越大一次BGSAVE期间发生写入越多COW就需要复制越多的内存页额外的内存开销也就越大。如果因为大量写入导致COW复制量过大加上fork时瞬间复制页表的开销主进程是有可能被阻塞的。我遇到过一台内存用在6GB左右的Redis配置了频繁的自动RDB高峰期每秒写入量很大结果BGSAVE期间出现主进程延迟飙升的告警。排查了半天最后定位是COW复制造成的额外内存和CPU压力。这也是我后来在关键业务上倾向使用AOF的原因之一。2.3 RDB的触发方式全集RDB这玩意儿不是你只能手动敲BGSAVE才生成的。梳理一下所有可能触发RDB生成的场景方便排查为什么磁盘上突然多了个rdb文件之类的问题SAVE命令同步生成线上禁用。BGSAVE命令手动触发异步快照用于备份时很常见。配置了save m n自动触发只要在m秒内有n次以上写入就会触发一次BGSAVE。主从全量复制的场景主节点在同步给从节点之前会主动做一次BGSAVE生成RDB文件发给从节点。执行SHUTDOWN关闭Redis且没开启AOF时系统会尝试做一次RDB快照再退出。执行DEBUG RELOAD类操作时会触发一次热重启并重新加载数据。默认的save配置一般是这样的save 3600 1 save 300 100 save 60 10000意思是3600秒内至少有1次写入或者300秒内至少有100次写入或者60秒内至少有一万次写入满足任意一条就触发一次快照。这套默认配置其实比较保守只在写入频率很高时才会频繁快照。但对很多业务来说如果60秒内有1万次写入就触发一次RDB那么最坏情况下可能丢一分钟数据这对某些场景是不可接受的也是我建议走AOF的原因。2.4 RDB方案的优势和坑RDB的优点很明确第一恢复速度极快。RDB文件是二进制格式加载时直接顺序读入内存重建数据对大实例来说比AOF重放一堆命令快得多。第二文件体积紧凑适合做备份和传输。第三对写性能影响相对较小毕竟真正的写入动作在子进程里。但它的缺点同样明显一是数据丢失窗口大默认配置下可能丢十几分钟甚至更久的数据。二是fork阻塞问题实例内存越大fork瞬间的耗时和COW压力越容易让主进程抖一下。三是全量快照模式在数据量大的时候并不优雅每次都是把整个内存写一遍对IO和磁盘空间的要求都不低。所以RDB更适合作为数据备份手段而不是唯一的数据安全兜底方式。3. AOF持久化把每一次数据变更都记成流水账3.1 AOF的完整写入链路AOF的全称是Append Only File思路和RDB完全不同。它不拍快照而是把每一条可能修改数据的写命令记录下来追加到文件末尾。你可以把它理解成记账每花一笔钱就记一行账本完整记录所有流水。以后要恢复数据不需要找旧账本直接重新过一遍账就行。一条写命令从客户端发过来到真正落盘大致经过这几个环节客户端发送SET之类的写命令给Redis。Redis执行命令修改内存数据。同时将这条命令追加到内存中的aof_buf缓冲区。在事件循环中根据appendfsync策略决定何时把aof_buf里的内容写到操作系统PageCache。操作系统PageCache中的数据什么时候真正刷到磁盘文件同样受策略控制。关键点在于命令先落到PageCache这一步并不代表数据已经安全落盘了。PageCache是操作系统管理的内存级缓存如果这一刻机器断电PageCache里还没刷盘的数据照样会丢。真正决定数据安全性的是后面那个fsync动作也就是强制把PageCache刷新到磁盘。这就像你在手机上记了一个备注第一步存在手机内存里PageCache稍后系统才同步到云端磁盘。如果这中间手机突然坏了云端没同步上的内容就没了。所以AOF的策略核心就是控制从内存到磁盘这一步的节奏。3.2 appendfsync的三种策略always、everysec、noappendfsync这个配置直接决定了AOF的安全性等级和性能损耗是AOF方案里最需要认真权衡的参数。配置项触发时机数据安全性性能影响最坏丢失窗口always每条写命令执行后立即fsync极高基本只丢该命令本身未成功写入的情况最差每次写都要等磁盘IO完成吞吐大打折扣近似0实际最多一条命令everysec每秒刷一次磁盘高极端情况丢1秒内的写入很好聚合刷盘开销可接受最多1秒数据no完全交给操作系统决定刷盘时机最低可能丢几十秒甚至更多数据最好几乎没有额外等待由系统缓存刷新策略决定通常较大如果你对Redis有一点了解肯定知道网上普遍推荐everysec。这个策略的性价比确实是最高的性能上把fsync频率压缩到每秒一次避免每条命令都同步等待磁盘安全上最坏情况只丢1秒数据。绝大多数业务能接受崩溃时丢1秒内的写入这个代价。always不是不能用但要清楚代价。一旦开启每一笔写请求都要等待真正的落盘高并发下Redis的QPS会明显下降而且磁盘性能会成为绝对瓶颈。实测下来同样一台机器从everysec改成always写入吞吐可能掉一个数量级。所以除非是那种绝对不能丢任何一条数据的强一致场景否则我不建议全员always。no这种策略就有点赌徒心态了把命运交给操作系统。操作系统PageCache攒够一批数据才会刷到磁盘一旦宕机丢失的数据量可能非常可观。这个选项我基本只在测试环境里见过生产环境尽量别碰。3.3 AOF重写机制与混合持久化日志文件一直往里追加文件会无限增长时间一长磁盘空间扛不住而且恢复时要重放大量命令启动会越来越慢。为了解决这个问题Redis引入了AOF重写机制。重写不是对原AOF文件做压缩修改而是基于当前内存里的数据重新生成一份最精简的写命令集合。举个例子你的key从初始值0执行了10万次INCR最终值100000。原AOF文件里会累积10万条INCR命令重写后只需要一条SET key 100000就能表达当前状态。子进程在重写期间父进程仍然在接收新的写命令这部分增量会同时进入一个重写缓冲区。子进程生成完新文件后把重写缓冲区的增量命令追加进去最后原子替换掉旧AOF文件整个过程对客户端基本无感。Redis 4.0之后官方又引入了混合持久化方案配置项是aof-use-rdb-preamble。开启之后AOF重写生成的文件不再是纯文本命令而是以RDB二进制格式保存当前全量数据放在文件开头后面再追加重写期间产生的增量AOF命令。这个设计非常聪明加载时先快速读取RDB部分完成全量恢复再回放少量AOF增量补齐最新数据恢复速度和数据安全性都照顾到了。这也是我前面说默认组合就是AOF混合持久化的原因。3.4 AOF方案的优缺点AOF最大的优势是数据安全性和灵活性。everysec策略下最多丢1秒数据always下基本不丢数据比RDB那个动辄丢几分钟数据的窗口强太多了。而且纯AOF文件是文本格式虽然混合模式下开头是二进制块但整体上可以用工具去分析甚至手动删掉一些有问题的危险命令这对应急修复很有价值。代价就是文件体积天然比RDB大写放大问题更明显纯AOF模式下恢复速度也远不如RDB。想象一下从几百MB的AOF文件里一条条重放命令和直接从几十MB的RDB二进制文件里加载差距是数量级的。这也是为什么生产中都不太用纯AOF而是开启混合持久化来弥补这个短板。4. RDB vs AOF终极对比到底怎么选4.1 一张表看清两者的本质差异对比维度RDBAOF存储内容全量数据二进制快照写命令追加日志恢复速度极快慢开启混合持久化后明显改善数据安全性差可能丢最后一次快照之后的所有数据高everysec最多丢1秒always几乎不丢文件大小紧凑大天然膨胀依赖重写去瘦身对写性能影响低forkCOW在极端情况有阻塞风险中等取决于appendfsync策略对磁盘占用较低较高存在写放大备份与传输方便直接拷贝rdb文件麻烦文件大备份成本高运维复杂度低配置简单中重写、刷盘策略都需要关注适用场景备份、容灾、快速迁移数据安全要求高的业务数据这张表是我自己的经验总结不是单纯翻文档抄来的。核心差异可以浓缩成一句话RDB赌的是机器不会突然死AOF赌的是最多丢一秒数据。前者换来的是又快又省后者换来的是安全可控。4.2 不同业务场景的选型建议纯缓存场景数据可重建、丢失无感。这种情况下很多人会直接关掉持久化换取最高性能。我只提醒一句如果这个实例还要承担主从复制的角色关闭持久化会让从节点同步也变得脆弱至少保留RDB兜底。常规业务缓存场景比如用户会话、临时标记、最新榜单这类。推荐AOFeverysec混合持久化。恢复速度不会太差数据安全也有保障是我最常用的一套组合。强一致、金融级业务场景比如账户余额、交易流水、分布式锁等。AOFalways是更稳妥的选择但务必提前做好性能压测。这里的逻辑不是让Redis替代数据库而是在数据库之外多一层保护。数据库替代场景Redis作为主存储直接扛业务请假想最坏情况。我的建议是AOF定期RDB从节点定期远程备份四件套都上。AOF保证崩溃后最多丢1秒数据RDB用于快速恢复和容灾从节点用于高可用切换远程备份防止机房本身出问题。4.3 主从复制与集群环境下必须注意的持久化细节很多人在单机场景下纠结半天一到主从、哨兵、集群架构下反而容易忽视持久化。这里有几个非常关键的点希望大家重视。第一主节点如果关了持久化全量同步给从节点的数据就完全依赖从节点的RDB快照。一旦主节点崩了哨兵切换从节点这个从节点可能还停留在很久以前的数据状态或者干脆就没有数据。你以为是高可用实际上一切换就是一次大范围数据丢失。第二哨兵在选主时会优先选择复制偏移量最大的从节点也就是数据最新的从节点。但如果所有节点都没持久化那么这个最新也只是内存里的最新重启之后大家都可能变成没有数据。这就是典型的高可用架构扛得住节点故障扛不住所有节点同时重启的场景。第三用Redis做分布式锁的朋友尤其要注意。很多人用Redisson或者自己封装SET NX实现锁把锁的key放在Redis里觉得集群部署了就很稳。可如果Redis没有持久化实例重启后锁信息全没。一个线程刚拿到锁还没执行完另一个线程同样能拿到锁两个线程同时进入临界区操作共享资源后果可能比锁失效更严重。我在生产环境就实际遇到过一次后面排障章节详细讲。5. 生产环境配置与运维实操5.1 redis.conf核心配置逐项说明以下是生产环境中我常用的持久化相关配置配合注释说明每一项的意义# RDB相关配置 # 自动快照触发条件 save 3600 1 save 300 100 save 60 10000 # 快照出错后是否拒绝写入 # yes表示bgsave失败时拒绝新写入宁可先停下来也不要让系统带病运行 stop-writes-on-bgsave-error yes # RDB文件是否压缩 rdbcompression yes # RDB文件与日志文件所在目录 dir /var/lib/redis # RDB文件名 dbfilename dump.rdb # AOF相关配置 # 开启AOF appendonly yes # AOF文件名 appendfilename appendonly.aof # 刷盘策略每秒刷一次 appendfsync everysec # 重写期间是否暂停fsync # 默认no即正常执行fsync避免重写导致AOF落后太多 no-appendfsync-on-rewrite no # 触发自动重写文件比上次重写后增长了100%且至少64MB auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb # AOF文件加载时遇到截断是否自动恢复 aof-load-truncated yes # 混合持久化开关 aof-use-rdb-preamble yes几个容易踩坑的配置点多说一句。stop-writes-on-bgsave-error这个配置默认是yes。它的意思是如果RDB快照写入失败比如磁盘满了Redis会拒绝新的写入请求。这个设计看着很激进但逻辑是既然快照已经写不进去了继续写入只会让内存数据和磁盘数据差距越来越大后面更难恢复。它在关键时刻能保护你也可能会在磁盘故障时突然导致线上写入失败所以平时要重点监控磁盘状态。auto-aof-rewrite-percentage 100和auto-aof-rewrite-min-size 64mb决定了AOF自动重写的频率。若AOF文件增长到上一次重写后大小的两倍且超过64MB就会自动触发重写。如果你发现重写太频繁可以调大百分比如果发现AOF文件一直膨胀不重写通常是重写期间子进程持续出问题需要手动检查。5.2 Redis启动时的数据加载顺序很多人以为Redis启动后先加载RDB再加载AOF其实不是。Redis启动时如果appendonly是开启状态它会优先加载AOF文件来恢复数据哪怕磁盘上同时存在RDB文件也忽略RDB只有当AOF完全关闭时才会走RDB加载流程。为什么这么做因为AOF文件通常包含Redis宕机之前的所有写命令数据更新用它恢复的数据更接近崩溃时的状态。而RDB文件可能是十几分钟前甚至更久之前的快照用它恢复会丢掉快照之后的所有修改。所以在混合持久化开启的情况下一个AOF文件内部就带着RDB格式的全量数据和AOF增量加载效率和数据新鲜度都是最优的。启动过程中可以通过Redis日志确认到底加载了哪个文件。比如看到类似DB loaded from append only file或者DB loaded from disk这样的日志就明白是从哪个持久化渠道恢复的了。排查为什么重启后数据是旧的这个问题时第一步就是看这条日志。5.3 手动备份与恢复的标准操作持久化文件的好处之一是可以做离线备份。我通常的做法是先执行一次BGSAVE等待RDB快照生成完毕后把RDB文件拷贝到备份目录或者异地存储。AOF文件如果开启了Append操作不能直接拷贝带写入状态的文件最好先执行一次BGREWRITEAOF让文件处于一个比较干净的状态再拷贝。# 手动触发RDB快照 redis-cli bgsave # 等待完成后拷贝文件 cp /var/lib/redis/dump.rdb /backup/redis/dump-20250117.rdb # 手动触发AOF重写优化文件大小 redis-cli bgrewriteaof # 参考在线导出RDB快照 redis-cli --rdb /backup/redis/dump-online.rdb恢复的时候更简单把RDB文件或AOF文件放到Redis配置的dir目录下文件命名和dbfilename或appendfilename保持一致直接启动Redis它会自动加载。恢复前建议先备份原文件防止当前生产环境的文件被意外覆盖。5.4 监控持久化状态我在线上排查持久化问题时最常用的命令是INFO persistence它能输出RDB和AOF最近一次执行的状态信息包括最近一次RDB是否成功、AOF重写是否在运行、AOF缓冲区大小等。这套输出是判断持久化是否健康的第一手依据。redis-cli info persistence输出里重点看几个字段rdb_last_bgsave_time_sec最近一次RDB快照耗时如果数值异常大说明磁盘压力大或者COW开销高。rdb_last_bgsave_status最近一次BGSAVE是否ok。aof_last_bgrewrite_status最近一次AOF重写是否ok。aof_last_write_status上次AOF落盘是否成功这里如果失败要立即排查磁盘。另外日志也是重要的信息来源。AOF写入失败、RDB快照失败这类错误通常都会打日志。日志里反复出现写盘失败相关的关键词基本可以判定持久化链路有问题。6. 常见故障与排查实录6.1 问题速查表根据我平时维护Redis的实战经验整理了一份高频问题的快速排查表方便大家遇到问题时直接对照处理。现象可能原因处理建议重启后数据是旧数据加载了RDB而不是AOF或AOF文件日期比RDB旧确认appendonly是否为yes优先加载AOF重启后直接丢大量数据AOF文件损坏Redis启动时默认忽略截断部分手动用redis-check-aof修复检查日志里truncated提示启动直接失败AOF加载报错混合文件头部损坏、版本不一致备份原文件后尝试用redis-check-aof --fix修复客户端大量写入报错磁盘满stop-writes-on-bgsave-error生效清理磁盘、扩容、修复快照失败原因频繁RDB快照阻塞主线程实例内存过大fork和COW开销高关闭THP降低save频率拆大key或者切换AOFAOF文件暴涨不自动重写重写条件没满足或重写过程不断失败手动BGREWRITEAOF检查子进程日志和磁盘空间持久化文件与内存数据不一致混合持久化未开启AOF丢失了崩溃前最后一小段时间开启混合持久化考虑提升appendfsync级别6.2 一次分布式锁失效的故障复盘前面提到分布式锁的场景这里详细说说我碰到过的一次真实故障。某服务的业务代码依赖Redis分布式锁来防止多个实例同时处理同一笔订单。锁的实现就是在Redis里写入一个带过期时间的key。当时这套Redis是主从架构主节点没有开启任何持久化从节点倒是开着AOF但不承担读写。某天机房短暂断电主节点和从节点几乎同时重启。因为主节点没有持久化文件启动后是空库从节点靠AOF恢复了大部分数据但哨兵最终把主节点切换到了那台重启后无数据的主节点上。结果就是锁key全部丢失所有业务实例同时成功获取到分布式锁多个线程同时进入唯一的资源处理逻辑大量订单被重复处理还有一部分数据因为并发写产生了脏数据。复盘的时候核心教训有两条。一条是分布式锁这种要求只要我没释放别人一定拿不到的语义光靠Redis单机持久化都不够稳至少要从两个维度去加强一是持久化配置强制打开且可靠二是锁的获取要配合fencing token之类的机制去防止长GC后锁过期导致的重入问题。另一条是不要在主节点上为了性能关掉持久化尤其是在分布式锁这种对一致性敏感的场景里Redis持久化不先保住高可用架构就是纸糊的。从那之后我这边凡是承接分布式锁的Redis实例一律开启混合持久化appendfsync至少everysec条件允许就用always。6.3 实用排障修复命令遇到持久化文件损坏的问题Redis自带两个修复工具我用过多次效果不错。# 修复AOF文件会移除损坏的尾部数据 redis-check-aof --fix appendonly.aof # 检查RDB文件完整性和内容 redis-check-rdb dump.rdb用redis-check-aof修复完文件之后一定要先备份原文件再启动Redis。修复过程会裁剪掉AOF尾部无法解析的部分虽然在绝大多数情况下丢失的只是最后几秒的增量数据但这个操作本身是不可逆的。另外还有一个很好用的技巧如果AOF文件已经完全损坏无法修复但RDB文件还是好的可以临时关闭AOF用RDB恢复数据等实例起来之后再重新开启AOF。这需要操作顺序严谨避免启动时又去加载坏掉的AOF导致失败。排查持久化为什么失败这类问题时日志是最高优先级的线索。Redis在启动和运行过程中会把加载状态、刷盘失败、快照失败这些信息都记录在日志里定位时先看日志最省时间。最后再聊一点个人体会。做了这么多年Redis运维我最大的感觉是持久化选型没有银弹关键是在安全、性能、恢复速度之间找到自己业务能接受的最小损失窗口。与其背一堆概念不如自己动手做一次恢复演练把RDB和AOF文件拷走直接kill -9把Redis杀掉再换个新目录启动看看数据能恢复到什么程度。这个测试做一次你就能切身体会到两种方案的真实差距也能真正理解该怎么选、怎么配。