Redis作为内存数据库,一旦进程崩溃、机器断电或者有人误执行了清库命令,数据就可能瞬间消失。但只要持久化配置得当,大多数情况下数据是可以找回的。本文记录一次完整的Redis数据丢失恢复演练,从模拟故障到最终数据找回,把每一步的操作和背后的原理都讲清楚,方便你在真实事故发生时能沉着应对。

一、先弄清数据为什么会丢
在动手恢复之前,必须先判断数据丢失的类型,因为不同类型的丢失,恢复手段完全不同。第一种是持久化文件损坏,比如服务器突然断电导致RDB文件写了一半,Redis重启时加载失败。第二种是误操作,最典型的就是运维人员在错误的实例上执行了flushall或者flushdb,这类命令会同时清空内存数据和AOF文件末尾的记录,处理起来最棘手。第三种是主从切换导致的数据回退,比如主库挂了之后从库顶上来,但这个从库的数据比主库旧,部分新写入的数据就“消失”了。第四种是持久化根本没有开启,实例一直在纯内存模式下运行,这种情况基本无法从Redis自身找回,只能依赖上游的业务数据库。
可以用info persistence命令快速判断实例的持久化状态,重点看rdb_last_bgsave_status和aof_enabled这两个字段。如果rdb_last_bgsave_status显示err,说明最近一次bgsave失败了;如果aof_enabled为0,说明AOF根本没开。演练开始前,先在测试环境确认这两项配置都是正常的,再进行下一步。
二、演练准备:搭建环境并制造数据
准备一个测试用的Redis实例,版本建议用4.0以上,因为后续会用到混合持久化特性。先写入一批测试数据,然后分别开启RDB和AOF,模拟一个配置健康的线上实例。
# 写入测试数据 redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379> set user:1001 "zhangsan" OK 127.0.0.1:6379> set user:1002 "lisi" OK 127.0.0.1:6379> lpush orders "order-001" "order-002" (integer) 2 127.0.0.1:6379> config set appendonly yes OK 127.0.0.1:6379> save OK</code>
执行save后,数据目录下会生成dump.rdb文件,同时因为开启了AOF,还会生成appendonly.aof文件。用config get dir确认数据目录位置,把这个目录整体备份一份,这一步非常关键——恢复操作之前一定要先备份现场,避免二次破坏。
接着模拟故障场景。这里选择最有代表性的两种:一是直接kill掉Redis进程模拟宕机,二是执行flushall模拟误删。两种场景分别演练,恢复思路完全不同。
三、场景一:利用RDB快照恢复宕机丢失的数据
Redis进程被kill后,如果执行的是kill -9,内存数据瞬间消失,此时能否恢复取决于持久化文件。AOF优先级高于RDB,只要AOF开启,Redis启动时会优先加载AOF文件。如果AOF文件完整,直接重启实例即可自动恢复:
# 检查AOF文件是否完整 redis-check-aof --fix appendonly.aof # 重启实例 redis-server /etc/redis/redis.conf # 验证数据 redis-cli -h 127.0.0.1 get user:1001 "zhangsan"
如果AOF文件损坏,redis-check-aof工具会提示截断位置,选择yes让工具自动修复,丢弃损坏部分之后的数据。代价是可能丢失最后一小段写入,但绝大部分数据能保住。修复完再启动Redis,数据就从AOF日志里重放回来了。
假如实例只开了RDB没有开AOF,恢复方式就是让Redis启动时加载dump.rdb文件。Redis启动时如果发现数据目录下有dump.rdb,会自动载入。需要注意的是,如果dump.rdb也很旧,那能恢复到的就只是上次快照时刻的数据,快照之后的增量写入会永久丢失。这也是为什么生产环境强烈建议RDB和AOF同时开启,RDB用于冷备和快速恢复,AOF用于保证数据完整性,两者互为补充。
四、场景二:误执行flushall后的紧急抢救
这个场景最考验反应速度。flushall执行后,内存被清空,同时AOF文件末尾会追加一条FLUSHALL命令,dump.rdb文件本身还在磁盘上但已经过时(如果之后触发了bgsave,RDB也会被覆盖成空的)。此时千万不能重启实例,因为重启后Redis会加载AOF,重放到FLUSHALL这条命令,数据照样是空的。
正确做法是立即执行config set appendonly no关闭AOF写入,阻止后续触发,然后立刻执行debug reload让Redis从最近一次的RDB快照重新加载数据:
# 第一步:立刻关闭AOF,防止重启时重放FLUSHALL 127.0.0.1:6379> config set appendonly no OK # 第二步:从RDB快照重载数据 127.0.0.1:6379> debug reload OK # 第三步:验证数据是否回来了 127.0.0.1:6379> get user:1001 "zhangsan"
恢复成功的前提是磁盘上还留着包含完整数据的dump.rdb,也就是flushall之后没有触发过bgsave。所以平时可以把RDB快照文件定时复制到其他机器做异地备份,关键时刻这就是救命稻草。如果连RDB也保不住,最后的手段是从主库的从库、集群的其他分片或者业务方数据库反推数据了。
对于AOF文件,还可以用文本编辑工具打开,把末尾那行FLUSHALL命令手动删掉再保存,然后重启实例,让Redis重放删除后的命令流,同样能找回数据。这个方法适合AOF体积不大的场景,操作前记得备份原文件。
五、恢复之后必做的几件事
数据找回来只是第一步,更重要的是防止再次发生。第一件事是核查持久化配置,推荐开启混合持久化(aof-use-rdb-preamble yes),AOF重写时前半部分是RDB格式的全量数据,后半部分是增量命令,恢复速度和数据完整性都更好。第二件事是给高危命令改名或者禁用,比如在配置文件里把flushall改成一个没人猜得到的名字:
# redis.conf中禁用或改名危险命令 rename-command FLUSHALL "" rename-command FLUSHDB "b8f2c1d4_flushdb_9e7a" rename-command CONFIG "config_5f3a8b"
第三件事是建立定期备份机制,用crontab每小时执行一次bgsave然后把dump.rdb拷贝到备份服务器,保留最近N天的快照。第四件事是完善监控告警,对rdb_last_bgsave_status异常、AOF写盘失败、主从延迟过大等情况配置告警,很多数据丢失事故的根源都是持久化早就悄悄失败了却没人发现。做完这几步,整个恢复演练才算真正闭环。