多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Redis持久化机制实战:RDB与AOF怎么选,避免数据丢失陷阱

Redis持久化机制实战:RDB与AOF怎么选,避免数据丢失陷阱 Redis持久化机制实战拆解RDB和AOF到底怎么选用错了会丢数据先给结论Redis虽然叫缓存数据库但你要是真把它当纯缓存用、完全不开持久化那你就是拿生产数据在裸奔。Redis默认的数据全部存在内存里一旦进程崩溃、机器断电、或者Docker容器被误删内存里的数据说没就没。而RDB和AOF就是Redis官方提供的两套持久化方案用来把内存里的数据落盘保存让你在重启之后还能把数据找回来。这篇文章我不会只丢一张对比表给你我会把RDB和AOF的底层工作原理、触发机制、配置参数、恢复流程、坑点教训全部拆开讲透并结合我自己在生产环境里踩过的坑告诉你什么时候该用RDB、什么时候该用AOF、什么时候两个一起上。无论是面试要答Redis持久化机制这类高频题还是你正在给线上Redis做容灾方案这篇文章都值得你从头到尾看完。1.1 为什么Redis必须考虑持久化Redis作为内存数据库性能优势来源就是把数据全部放在内存中读写单线程模型加上IO多路复用轻松扛住十万级QPS。但内存有一个致命问题掉电即失。如果进程退出、机器重启、或者OOM被系统杀掉内存数据直接清空。很多人会说Redis就是给MySQL挡热数据的丢了就丢了回源数据库不就行了这话在纯缓存场景下成立但生产中Redis经常承担的不只是缓存比如分布式锁、排行榜、未读消息数、秒杀库存、用户会话、接口幂等记录这些数据一旦丢失要么造成业务逻辑错误要么需要大量请求穿透到数据库把数据库打崩。我见过不止一次因为Redis没开持久化、重启后缓存雪崩直接导致MySQL连接数被打满的线上事故。所以只要你的Redis里存了不应该随便丢的数据持久化就是必选项而不是可选项。RDB和AOF是Redis提供的两种落盘方案各有取舍接下来逐个拆解。1.2 先搞清楚两种机制各自是什么RDB全称Redis Database Backup File也就是Redis数据库快照。它的做法很简单粗暴在某个时间点把内存里的全量数据序列化后写到一个二进制压缩文件里默认文件名dump.rdb。你可以把它理解成给整个Redis内存拍了一张照片照片里是此刻所有key和value的完整状态。AOF全称Append Only File中文叫追加文件。它的思路完全不同Redis每次执行写命令SET、LPUSH、DEL等都会把这条命令追加到AOF文件末尾。恢复的时候只需要把AOF文件里记录的每一条命令重新执行一遍就能把数据还原回来。你可以把它理解成一本操作流水账每一步干了什么都记下来出事了按账本重放一遍。这两种机制的差异本质上就是定期全量快照和实时增量日志这两种思路的差异。理解了这一点后面所有的对比你都能顺着逻辑推出来。2. RDB快照全量落盘简单粗暴但要注意阻塞问题2.1 RDB的触发方式有哪几种RDB不是想存就存的它有明确的触发时机。手动触发有两个命令SAVE和BGSAVE。SAVE是同步阻塞式——Redis主进程直接做快照期间不处理任何命令你别在生产环境用这个数据量大了直接卡死业务。BGSAVE是后台异步式——主进程fork一个子进程出来由子进程负责写快照文件主进程继续接受客户端请求这是生产环境手动触发RDB的正确姿势。除了手动命令RDB还有自动触发机制核心配置是save参数。Redis默认配置里有三条save 900 1 save 300 10 save 60 10000这三条的意思是900秒内至少有1次写操作、或者300秒内至少有10次写操作、或者60秒内至少有10000次写操作满足任意一条就自动执行BGSAVE。这个设计很聪明它把数据变更的频率和数量都考虑进来了写得多就快照勤一点写得少就拉长快照间隔。你完全可以根据业务写入量调整这些阈值比如写频率高的业务可以改成。save 300 1 save 60 1000另外还有几个场景会自动触发RDB执行SHUTDOWN命令正常关闭Redis时如果没有开启AOF会执行一次快照主从复制场景下从节点首次全量同步时主节点会自动生成RDB文件发给从节点。搞清楚触发机制非常重要因为很多人以为我配置了save就得按这个频率生成快照但你得明白它依赖写操作量如果业务是读多写少可能一天下来也触发不了几次。2.2 RDB文件里到底存了什么恢复过程有多快RDB文件是一个经过压缩的二进制文件里面记录的是Redis内部的数据结构序列化结果。比如一个字符串key、一个哈希表、一个列表都以Redis底层的编码格式存进去。因为采用二进制格式且经过压缩RDB文件通常比同等数据量的AOF文件小得多传输和恢复都非常快。恢复过程就更直接了Redis启动时检测到dump.rdb文件存在直接把这个文件加载进内存相当于看照片恢复现场。因为不需要逐条执行命令加载速度非常快。我用一个1000万key、大约8GB数据的实例实测过RDB加载大概需要20到30秒而如果用AOF重放这些写入命令可能要几分钟甚至更久。为什么RDB恢复快因为它是结果导向。你不需要知道每一步怎么走过来的只需要知道最终长什么样。这也是RDB做主从复制的全量同步、做数据备份的最佳原因——文件小、传输快、恢复快。2.3 RDB的致命短板两次快照之间的数据会丢再先进的快照也不是实时的。只要不是每秒都做快照RDB就天然存在数据丢失窗口。比如你配置的是save 900 1那么在最坏情况下如果系统在第899秒崩溃最后一次快照后写入的所有数据全部丢失。哪怕你把save调短到每60秒一次依然会丢最多60秒的数据。对于某些业务60秒的数据量可能就是几万条订单记录这是不可接受的。还有一个容易被忽视的问题fork子进程的阻塞风险。BGSAVE虽然不阻塞主进程处理命令但fork这个动作本身在内存占用大的实例上会造成主进程短时间卡顿。Linux的fork需要复制页表如果Redis实例占用内存达到10GB以上fork瞬间可能造成几百毫秒到1秒的停顿。如果是高QPS业务这一秒的停顿可能引发大量超时。所以你以为的后台快照不影响服务实际上只是把影响降到了最低并没有完全消除。3. AOF日志每条写命令都记账数据安全性直接拉满3.1 AOF记录的是什么怎么从日志恢复数据AOF文件比RDB直观得多它本质上就是一个纯文本日志文件。你执行SET name xiaolinAOF文件里大概率会追加一条类似的命令记录。Redis 7.0之前AOF文件里存的是Redis协议格式命令7.0之后引入了AOF文件由base文件加增量日志组成的形式但核心逻辑不变记下每条改变数据的命令。恢复过程也不难猜Redis启动时如果检测到AOF文件存在会从头到尾逐条读取里面的写命令重新执行一遍。这就是按账本重放。这里有个关键点需要注意AOF重放的速度和写入时的命令数强相关。如果AOF文件里积攒了几百万条SET命令恢复时就要执行几百万次命令哪怕每次只要一微秒累积起来也够慢。所以AOF机制里必须有一个配套组件来防止日志无限膨胀这个组件就是AOF重写后面专门讲。3.2 appendfsync三种策略选错就是选数据还是选性能AOF是先把写命令写入操作系统内核缓冲区再由内核把缓冲区的数据刷到磁盘。这个过程叫作fsync。如果每次写命令都立刻fsync那和同步写磁盘没区别性能会降到惨不忍睹。但如果不fsync数据就停留在内核缓冲区里一旦机器断电缓冲区里的数据全会丢。Redis给了你三个选择appendfsync always appendfsync everysec appendfsync noalways代表每次写命令都同步刷盘最安全但性能最差实测QPS会掉一大截生产环境几乎没有人用。everysec代表每秒刷一次盘由后台线程统一执行fsync。这是性能和数据安全的最佳折中方案也是绝大多数生产环境的默认选择。极端情况下最多丢1秒的数据。no代表完全交给操作系统决定什么时候刷盘性能最好但数据安全性最差可能丢好几秒甚至几十秒的数据通常不推荐。我个人生产经验是everysec足够用了。首先Redis单机QPS上限摆在那每秒fsync一次对整体性能影响不大其次业务上如果你连丢1秒数据都接受不了说明你本身就不该把这类数据放在Redis里应该考虑MySQL这类磁盘数据库。选always的人往往是对数据安全有执念但你要知道代价是写性能可能直接下降一个数量级。3.3 AOF文件膨胀问题与重写机制的底层逻辑AOF是追加日志每一条写命令都往里加文件只会越来越大。比如你对同一个key执行了一万次INCRAOF文件里就会有一万条INCR记录。恢复这一万条记录还不如直接记一条最终的value。AOF重写就是来解决这个问题的。Redis的AOF重写机制是fork一个子进程读取内存中的当前数据状态生成一份最小的写命令集合并写入新的AOF文件。比如刚才那个被INCR一万次的key重写后只剩一条SET key value。重写完成后Redis会用新AOF文件替换旧文件。这个机制的触发条件由两个参数控制auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb意思是AOF文件比上次重写时增长了一倍100%并且文件大小超过64MB时自动触发重写。你还可以用BGREWRITEAOF命令手动触发重写。这里有一个很容易理解错的点AOF重写不是把旧AOF文件拿来压缩而是完全基于当前内存中的数据状态重新生成一份最小的日志。所以哪怕你之前删了一万个key重写后的AOF文件里根本不会出现那些已删除key的任何记录文件体积会大幅缩小。4. 正面硬刚RDB和AOF的八个维度对比4.1 先看一张对比表结论一目了然很多文章喜欢把RDB说得一无是处、把AOF捧上天这是不对的。两者各有最适用的场景下面这张表是我结合多年实践整理的直接收藏对比维度RDB快照AOF日志数据格式二进制压缩文件RD协议文本命令数据丢失窗口取决于save策略秒级起步everysec下最多丢1秒恢复速度极快直接加载文件较慢需逐条重放命令文件体积小二进制压缩大需重写控制膨胀对性能影响fork瞬间有阻塞风险写入有IO开销everysec影响可控适合场景备份、全量同步、容灾归档数据安全要求高、重启快速恢复可读性不可读可读方便审计和误操作排查启动优先级低Redis优先用AOF恢复高数据更完整这条启动优先级很多人不知道Redis启动时如果同时存在RDB文件和AOF文件会优先加载AOF文件来恢复数据因为AOF保存的数据通常更新、丢失窗口更小。4.2 恢复速度和安全性到底怎么权衡恢复速度方面RDB是压倒性优势。它加载的是压缩后的二进制内存快照相当于把持久化数据直接反序列化回内存IO量和解析成本都很低。AOF则要逐条执行文本命令每条命令都要走一遍命令解析、执行、参数校验的完整流程恢复时间随命令数线性增长。举一个我实际遇到过的场景6GB数据的Redis实例RDB恢复用了大约15秒AOF恢复用了将近3分钟。如果是大集群中某个节点宕机需要尽快拉起这3分钟的差距足以影响整个集群的可用性。安全性方面AOF在everysec策略下最多丢1秒数据RDB最好情况也取决于你的save策略默认配置下极端能丢15分钟甚至更久的数据。如果你的业务可以接受分钟级数据丢失用RDB完全没问题如果丢1秒都心疼必须上AOF。但这里有一个反直觉的经验RDB虽然丢的数据可能更多但它永远能恢复出一个一致的、完整的时间点状态。AOF虽然丢得少但在Redis异常退出、AOF文件写入半截的情况下如果配置不当或者文件损坏恢复过程可能报错甚至失败。所以AOF的安全性优势还必须仰仗你的配置和文件完整性保障。4.3 什么场景下应该用哪个什么场景下必须混合用我建议分三种情况判断如果你的是纯缓存Redis也就是所有数据都能从数据库回源丢了也没什么业务损失那开不开持久化其实无所谓。开了反而增加写盘开销。我见过不少团队为了省事默认开着一个RDB也还行但心里要清楚这不是为了恢复数据用的纯粹是主从复制需要全量同步文件。如果存的是会话、验证码、临时计数这种可以接受分钟级丢失、但不想一重启就全空的数据用RDB就够了。配置合理的话RDB文件小、备份方便你甚至可以每天把RDB文件同步到对象存储做归档。如果存的是订单状态、秒杀库存、用户余额这类不能丢太多数据的关键业务数据直接上AOF用everysec策略。这样即使实例崩溃重启后最多也就丢1秒数据绝大多数业务都扛得住。生产环境的高可用架构里我强烈建议两种都开启也就是RDB加AOF同时启用。RDB用来做冷备份和主从同步AOF用来保证极端情况下的数据完整性。这就是Redis 4.0之后推荐的混合持久化方案后面我专门讲。5. 混合持久化Redis 4.0之后的最优实践5.1 RDB加AOF为什么更好Redis又是怎么融合两者的既然RDB恢复快但丢数据多AOF恢复慢但丢数据少那能不能两者取长补短Redis 4.0给出的答案是用AOF作为主持久化但AOF文件的前半部分直接复用RDB格式。具体来说开启混合持久化后AOF重写时不再生成纯文本命令集合而是先生成一个RDB格式的二进制快照作为AOF文件的头部再把这个期间新产生的增量写命令以Redis协议追加在后面。这样做的好处一下子就体现出来了恢复数据时Redis先快速加载AOF文件头部的那段RDB快照把大部分数据瞬间恢复然后再重放后面一小段增量的命令日志即可。恢复速度接近纯RDB数据安全性接近纯AOF。文件体积也远比纯AOF小因为快照部分是压缩的二进制格式。开启方式非常简单在Redis配置文件里加上aof-use-rdb-preamble yesRedis 5.0以上版本默认就是开启状态。如果你用的是老版本Redis建议重点考虑升级混合持久化带来的收益太明显了。5.2 四步配置一套靠谱的混合持久化方案如果你准备在测试环境复现这套方案按下面四个步骤操作每一步我都标注了目标和验证方法第一步修改redis.conf设置持久化相关配置# RDB配置 save 900 1 save 300 10 save 60 10000 # AOF配置 appendonly yes appendfilename appendonly.aof appendfsync everysec auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb # 混合持久化 aof-use-rdb-preamble yes第二步重启Redis使配置生效然后执行info persistence命令确认appendonly为enabled状态rdb_bgsave_in_progress为0。第三步写入一批测试数据手动触发AOF重写redis-cli BGREWRITEAOF执行后查看AOF文件头部如果是开启混合持久化的Redishead命令看到的应该是乱码式的二进制内容而不是可读的文本命令流。这个验证方法很直观看到二进制就说明混合结构生效了。第四步做一次崩溃恢复演练直接kill掉Redis进程模拟突然宕机然后重新启动Redis。观察启动日志中加载AOF文件的时间再检查之前写入的数据是否完整存在。5.3 实际生产配置案例一套可以参考的持久化参数下面这组参数是我在一个日活百万级别、Redis承载会话加库存数据的项目里实际使用的配置你可以直接参考appendonly yes appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 128mb aof-use-rdb-preamble yes save 900 1 save 300 5 save 60 10000几个参数解释一下no-appendfsync-on-rewrite我保持no含义是AOF重写期间仍然正常fsync不为了重写性能而牺牲数据安全。auto-aof-rewrite-min-size调到128MB防止频繁自动重写导致IO压力过大。save改成300 5是因为这个业务在低峰期写操作频率较低如果保持默认的300秒内10次写可能在低峰期迟迟不触发快照。这套配置跑了大半年经历过数次非正常重启和一次服务器断电数据恢复到最多丢几秒完全满足业务SLA。6. 常见问题与排查技巧实录6.1 高并发写入下的持久化性能问题第一个高频问题开AOF之后写性能掉了很多怎么办先别急着关AOF先查两个点。第一确认appendfsync用的是everysec而不是alwaysalways导致同步刷盘的性能损失是数量级的。第二检查redis的日志里有没有Slow down类的警告如果有说明磁盘刷盘速度跟不上写入速度。这时候合理的解法是先换固态硬盘其次是调整AOF重写策略错开重写高峰。我自己踩过一次坑把Redis跑在一块机械硬盘上开了everysec平时没啥感觉一到大促流量进来AOF写入直接成为性能瓶颈TPS掉了一半。后来换成SSD问题立刻消失。持久化性能的第一瓶颈永远是磁盘的随机写和刷盘能力不要绕开这个根本原因去调什么乱七八糟的参数。6.2 AOF文件损坏了怎么办第二个高频问题服务器异常断电后重启Redis提示AOF文件校验失败。不用担心Redis自带修复工具。执行redis-check-aof --fix appendonly.aof这个命令会扫描AOF文件把损坏的尾部截掉保留完整部分。修复后Redis通常能启动成功但你要意识到被截掉的那部分数据就是丢失的数据。这个工具能修好99%的AOF损坏场景前提是你没关掉AOF文件里的校验和检查。另外还有一个场景你手动改坏了AOF文件Redis启动时可能直接拒绝启动提示Bad file format reading the append only file。别慌同样的修复命令跑一遍就行。如果修复后依然启动失败说明损坏比较严重这时候RDB文件就是你最后的救命稻草。6.3 RDB文件加载失败和fork阻塞问题第三种问题Redis启动时RDB文件加载失败。原因可能是磁盘坏道、文件被部分写入、或者Redis本身版本不对。先用redis-check-dump工具检查文件完整性redis-check-dump dump.rdb这个工具会输出RDB文件里加载到的所有key信息如果某个key位置报错说明文件损坏。处理办法通常是删除坏文件重新生成快照但要注意删除RDB文件意味着你放弃了最后一次快照之后的全部数据。所以在做这个操作前先确认你是否还有别的数据备份。关于fork阻塞我再多分享一个经验在主从架构里如果主节点内存特别大fork造成的阻塞可能导致主从延迟瞬间飙升甚至触发主从切换。解决思路是控制单个Redis实例的内存不要太离谱一般生产环境超过10GB就要考虑拆分实例或者用阿里云那种支持秒级fork的定制内核。超过20GB还想坚持单实例风险就非常大了。6.4 日常运维中建议固化的习惯最后分享几个我坚持了很久的运维习惯。第一每天凌晨用BGSAVE生成一次RDB快照文件然后脚本自动拷贝到独立的备份目录并保留最近7天的版本——这样即使Redis本身出了问题你手里也有一份可回滚的冷备。第二每次调整持久化配置后都要做一次完整的数据恢复演练只改配置不验证恢复效果等于白改。第三把info persistence输出的各个指标比如rdb_last_bgsave_status、rdb_last_bgsave_time_sec、aof_last_write_status纳入监控告警任何一个变红都要立即处理。这些习惯看着简单但关键时刻能救命。我经历过一次机房断电重启全部节点起来后由于AOF文件都完好数据几乎没有损失那一刻你就知道平时花在持久化运维上的时间一点都不亏。回到最开始的问题RDB和AOF有什么区别答案是它们分别代表了性能与备份便利和数据安全与实时性这两种不同的权衡。RDB恢复快、文件小、适合备份AOF丢数据少、日志可审计、适合关键业务。而聪明的做法从来不是二选一而是用RDB加AOF的混合持久化方案把两者的优势都拿到手。不同版本的Redis在持久化细节上还有不少差异比如Redis 7.0的AOF多文件架构、Redis 8.0引入的新特性这些都是值得你继续深入研究的方向。持久化机制是整个Redis可靠性的地基值得你多花时间把它彻底吃透。
返回列表