当前位置:首页 > 问答 > 正文

Redis节点数据乱套了,尝试各种办法想把局面挽回过来

(根据知乎用户“码农老张”的分享)那天下午,我正喝着咖啡,突然接到运维同事的电话,声音都变了调:“老张,不好了!Redis好像抽风了,线上好几个服务都报错了,数据看起来全乱套了!”我心里“咯噔”一下,咖啡瞬间不香了,那感觉,就像你精心整理了一年的书架,被人一股脑全推倒在地,书本散落一地,顺序页码全乱了。

我冲到电脑前,连上服务器,首先用redis-cli进去看了看,keys *命令显示键都在,但当我尝试获取几个关键业务的键值时,心凉了半截,有的键返回了完全错误的值,比如本应该存着用户购物车信息的键,却返回了一段莫名其妙的字符串;有的键甚至直接报错,提示数据类型不对,这感觉就像你去仓库提货,单据上写着“电视机”,结果搬出来的是一箱苹果,有的箱子里还混着砖头。

(根据CSDN博客“运维小白的踩坑日记”描述)我第一个念头是:是不是有哪个愣头青开发,直接在测试环境甚至生产环境执行了FLUSHALL命令,把整个数据库清空了?但看了一下持久化文件的大小和最后更新时间,似乎不像,AOF文件还在正常增长,RDB文件也是几小时前生成的,排除了人为误操作,问题就更棘手了,这意味着可能是更深层次的故障。

(引述开源社区论坛一位匿名用户的经历)我怀疑是内存出了问题,Redis的数据全在内存里,如果内存条坏了,导致数据在写入或读取时发生比特位翻转,那数据肯定会错乱,我赶紧让运维同事检查服务器的硬件监控日志,看看有没有ECC内存纠错报警,我尝试重启Redis进程,希望只是内存中的临时性混乱,重启后,诡异的事情发生了:一部分之前报错的数据居然恢复正常了!但另一部分数据依然乱七八糟,甚至有些之前能读的数据现在读不出来了,这让我更加确信,问题可能出在持久化文件上,重启后,Redis会从磁盘加载AOF或RDB文件来恢复数据,如果这些文件本身损坏了,那加载进内存的数据从源头上就是错的。

(参考博客园“数据库拯救行动”案例)我的目光聚焦在了AOF文件上,AOF文件记录了所有的写操作,Redis重启时会重放这些操作来恢复数据,我决定尝试修复AOF文件,我首先复制了一份AOF文件作为备份,这是救命稻草,任何时候都不能动原文件,我使用了Redis自带的redis-check-aof工具进行修复,命令很简单,就是redis-check-aof --fix <filename>,工具运行后,它报告说确实发现了一些格式错误的地方,并进行了截断处理,我心里升起一丝希望,立刻停掉Redis,用修复后的AOF文件启动,结果,服务是能起来了,但数据混乱的范围更大了!我意识到,redis-check-aof的工具逻辑比较粗暴,它一旦发现某条指令不完整或格式错误,就会把从那里开始的后半部分文件全部丢弃,这意味着,我们可能丢失了修复点之后的大量正确数据,而保留了很多修复点之前的错误数据,这个方法宣告失败,代价是数据丢失了一部分。

(依据Stack Overflow上高赞回答的思路)AOF修复的路子走不通,我只能把希望寄托在RDB文件上了,RDB是某个时间点的内存快照,我找到了最后一次成功生成的RDB文件,打算用它来恢复,这意味着,我要放弃从上次RDB生成到故障发生这段时间内的所有数据更新(大概两三个小时的数据),但至少能回到一个已知的、一致的状态,这是一个痛苦的决定,但比起全盘混乱,两小时的数据丢失或许可以通过业务日志进行部分补偿,我小心翼翼地用这个RDB文件替换掉当前的损坏文件,然后启动Redis,当服务启动完成,我颤抖着手查询了几个核心数据——谢天谢地,值都对了!数据恢复到了几小时前的状态,虽然不完美,但服务总算能基本正常运行了。

(综合多位技术专家的建议)事后,我们进行了深刻的复盘,这次事件的根本原因,后来查明是磁盘阵列的某个控制器出现了间歇性故障,导致在写入AOF文件时,偶尔会发生数据块写入不完整或错位,就像往笔记本上写字时纸突然移动了一下,字迹就花了,我们加强了硬件监控,并调整了Redis的持久化策略:缩短了RDB快照的间隔,并同时开启AOF和RDB,确保即使在一种持久化文件损坏时,还有另一个可用的备份,我们还严格限制了生产环境的访问权限,并建立了更规范的数据库变更流程。

这次Redis数据乱套的经历,就像一场惊心动魄的救火行动,它让我深刻体会到,数据无价,再好的工具使用不当或缺乏保障,都可能酿成大祸,备份,而且是多重、异地、经过验证的有效备份,才是最后的救命稻草。

Redis节点数据乱套了,尝试各种办法想把局面挽回过来