别再用 BGSAVE 敷衍了事!生产环境 Redis 备份恢复的血泪实战指南
大家好,我是你们的老朋友。今天想和大家聊聊 Redis 的备份恢复——这个看似简单、实则坑多到能埋人的活儿。
很多人一提到 Redis 备份,张口就是“开个 BGSAVE 不就完事了?”、“RDB 文件不就是备份吗?”……说真的,我以前也这么天真过。直到某天凌晨三点,线上 Redis 集群主节点宕机,RDB 文件损坏,AOF 也没开,业务数据丢了大半,老板站在身后盯着屏幕问:“你不是说 Redis 很稳吗?”
那一刻,我才明白:在生产环境里,备份不是功能,而是责任。
你以为的备份,可能根本不算备份
先泼盆冷水:RDB 快照 ≠ 可靠备份。
Redis 默认的 RDB 快照(比如 save 900 1)只是把内存数据 dump 到磁盘,但它有几个致命问题:
- 单点存储:RDB 文件就躺在本地磁盘,万一机器挂了、磁盘坏了,文件直接蒸发。
- 无版本管理:每次 BGSAVE 都会覆盖旧文件,你想回滚到昨天的数据?没门。
- 一致性风险:BGSAVE 是 fork 子进程做的,如果期间主进程写压力极大,fork 可能卡住,甚至 OOM。
- 恢复慢:大实例(比如 20GB+)加载 RDB 要几分钟,业务等得起?
我们曾经有个服务,Redis 实例 30GB,RDB 文件每天凌晨生成一次,存本地。结果某次磁盘故障,整个实例连同备份一起没了。虽然有从节点,但主从切换后发现从节点的数据也落后了 40 分钟——因为主节点在宕机前已经无法同步。
所以,真正的生产级备份,必须满足三个条件:
- 异地存储(至少不在同一台机器)
- 可验证恢复(不能只存不管能不能用)
- 保留多个时间点(支持按需回滚)
我们的生产备份方案:RDB + AOF + 对象存储 + 自动校验
经过几次“事故教育”,我们现在对核心 Redis 实例采用四重保障策略:
1. 开启 AOF + everysec 模式
虽然 AOF 文件比 RDB 大,恢复慢一点,但它记录的是操作日志,理论上可以做到秒级数据恢复。我们配置:
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb注意:不要用 always!性能损耗太大,everysec 是性价比最高的选择。
2. 定时触发 BGSAVE,并上传到对象存储
我们写了个脚本,每天凌晨 2 点强制执行 BGSAVE,等生成完成后,把 .rdb 和 .aof 文件打包,上传到阿里云 OSS(或 AWS S3、MinIO 等)。
关键点来了:不能直接传本地 RDB 文件!因为 Redis 运行时,RDB 文件可能正在被写入(比如 rewrite),直接 cp 可能损坏。
正确做法是:
# 先让 Redis 生成新快照
redis-cli -h $HOST -p $PORT BGSAVE
# 等待快照完成(检查 info Persistence 里的 bgsave_in_progress)
while true; do
status=$(redis-cli -h $HOST -p $PORT INFO PERSISTENCE | grep bgsave_in_progress)
if [[ "$status" == "bgsave_in_progress:0" ]]; then
break
fi
sleep 5
done
# 获取最新的 dump.rdb 路径(从 config get dir/dbfilename 拿)
DIR=$(redis-cli -h $HOST -p $PORT CONFIG GET dir | tail -1)
FILE=$(redis-cli -h $HOST -p $PORT CONFIG GET dbfilename | tail -1)
FULL_PATH="$DIR/$FILE"
# 复制到临时目录(避免后续 rewrite 覆盖)
cp "$FULL_PATH" /tmp/redis_backup_$(date +%Y%m%d).rdb
# 同样处理 AOF 文件(如果有)
if [ -f "$DIR/appendonly.aof" ]; then
cp "$DIR/appendonly.aof" /tmp/redis_aof_$(date +%Y%m%d).aof
fi
# 打包上传
tar -czf redis_backup_$(date +%Y%m%d).tar.gz /tmp/redis_backup_*.rdb /tmp/redis_aof_*.aof
ossutil cp redis_backup_*.tar.gz oss://your-bucket/redis-backups/$HOST:$PORT/这个脚本跑在独立的备份服务器上,和 Redis 实例物理隔离。
3. 保留 7 天历史 + 自动清理
OSS 上我们用生命周期规则自动删除 7 天前的备份。同时,脚本里加了日志记录和失败告警——如果上传失败,企业微信机器人立刻@我。
4. 每月做一次恢复演练
最怕什么?备份了一堆,真出事发现全打不开。
所以我们每月挑一个非核心实例,从 OSS 下载备份,在测试环境完整恢复,验证数据是否一致、服务能否启动。
有一次演练就发现:AOF 文件末尾有截断,原因是 Redis 在 shutdown 时没正常关闭。后来我们在脚本里加了 redis-cli SHUTDOWN SAVE 来确保干净退出(仅用于演练环境,生产慎用)。
灾难恢复:怎么把备份真正用起来?
假设现在 Redis 主节点彻底挂了,磁盘数据全丢,怎么办?
Step 1:从对象存储下载最新备份
ossutil cp oss://your-bucket/redis-backups/10.0.0.10:6379/redis_backup_20240601.tar.gz ./
tar -xzf redis_backup_20240601.tar.gzStep 2:启动一个临时 Redis 实例指向备份文件
新建一个 redis.conf:
port 6380
dir /data/restore
dbfilename redis_backup_20240601.rdb
appendonly yes
appendfilename "redis_aof_20240601.aof"把解压出来的 RDB 和 AOF 放到 /data/restore/,然后启动:
redis-server ./redis.confRedis 会先加载 RDB,再重放 AOF 日志,最终得到接近故障前的状态。
Step 3:验证数据
用 redis-cli -p 6380 KEYS * 或业务脚本抽查关键 key,确认数据完整。
Step 4:切流量 or 数据迁移
- 如果是单机 Redis,可以直接替换原实例(停掉旧进程,改端口,重启)。
- 如果是集群,需要用
redis-cli --cluster import把数据导入新集群,或者用MIGRATE命令逐 key 迁移(适合小数据量)。
⚠️ 注意:AOF 重放可能会很慢!30GB 的 AOF 文件,重放可能要 20 分钟。所以RDB + AOF 组合中,RDB 是基础,AOF 是增量补偿,尽量让 RDB 尽可能新。
那些年踩过的坑
- 坑1:以为从节点能当备份
从节点确实能 failover,但它不是备份!主节点误删FLUSHALL,从节点也会同步删除。备份必须是静态快照,不受实时操作影响。 - 坑2:备份脚本没处理权限问题
有次脚本在 root 下运行,生成的 RDB 文件属主是 root,Redis 进程没权限读,恢复时直接报错。后来统一用redis用户运行脚本。 - 坑3:没监控备份大小
有次业务异常写入大量垃圾数据,RDB 从 5GB 暴涨到 50GB,OSS 费用翻了十倍。现在我们加了备份大小告警:如果比昨天大 200%,就报警。 - 坑4:跨版本恢复失败
Redis 6 的 RDB 文件在 Redis 5 上无法加载。现在我们要求备份脚本记录 Redis 版本号,恢复时必须匹配。
高级玩法:用 redis-rdb-tools 做增量分析
有时候不需要全量恢复,只想找回某个被删的 key。这时候可以用 redis-rdb-tools 解析 RDB 文件:
rdb --command json dump.rdb > data.json
grep "user:12345" data.json或者生成 diff:
rdb -c diff yesterday.rdb > yesterday.diff
rdb -c diff today.rdb > today.diff
diff yesterday.diff today.diff这对排查“谁删了我的数据”特别有用。
最后说几句
Redis 很快,但快不代表不会丢数据。备份不是技术问题,是流程问题。你有没有自动化?有没有验证?有没有异地?有没有权限隔离?这些才是关键。
别等到凌晨三点才想起备份的事。花一天时间搭好备份体系,能让你未来三年睡得踏实。
如果你觉得这篇文章帮你避开了未来的坑,欢迎转发给那个总说“Redis 不用备份”的同事。也欢迎关注我的公众号,我会继续分享更多真实、带血、能落地的运维经验。
毕竟,我们不是在写代码,是在守护业务的生命线。
公众号:运维躬行录
个人博客:躬行笔记