Redis 从 0 到 1(三十三):机器断电 Redis 起不来——AOF/RDB 损坏的 4 步抢救流程

我不会起名字322 · 后端 / 算法 / 数据库

📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL

文章目录

  • [Redis 从 0 到 1(三十三):机器断电 Redis 起不来------AOF/RDB 损坏的 4 步抢救流程](#Redis 从 0 到 1(三十三):机器断电 Redis 起不来——AOF/RDB 损坏的 4 步抢救流程)
    • 一、现象:一行报错,全系统停摆
    • [二、4 步抢救流程(收藏点 1)](#二、4 步抢救流程(收藏点 1))
    • [三、Go 实操:抢救后数据完整度核对(收藏点 2)](#三、Go 实操:抢救后数据完整度核对(收藏点 2))
    • 四、命令清单
    • [五、排查步骤:Redis 拒绝启动](#五、排查步骤:Redis 拒绝启动)
    • 六、总结

Redis 从 0 到 1(三十三):机器断电 Redis 起不来------AOF/RDB 损坏的 4 步抢救流程

上一篇《Redis 从 0 到 1(三十二):Cluster 节点宕机,请求疯狂报 MOVED------Go 客户端 4 个适配点》我们稳住了拓扑变化,链接:https://blog.csdn.net/2501_92769340/article/details/167400645 。

本案最硬核:机房断电后 Redis 拒绝启动,日志只有一行 Bad file format reading the append only file------数据就在坏文件里,怎么救?

一、现象:一行报错,全系统停摆

断电恢复后,redis-server 启动即退出:

复制代码
* Reading RDB preamble from AOF file...
# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>

AOF 是命令日志(第 7 篇),断电瞬间写入缓冲区的半个命令、或文件尾部的残缺 MULTI 块,都会让启动时的"重放校验"直接失败------Redis 不敢猜你想说什么,宁可拒绝启动。这不是 Redis 脆,是它在保护你不启动一个数据不一致的实例。

二、4 步抢救流程(收藏点 1)

铁律:先备份,再动任何文件。--fix 的代价是丢弃无法解析的部分,原文件是唯一后悔药。

bash 复制代码
# 第 1 步:备份(!)
cp /var/lib/redis/appendonly.aof /backup/appendonly.aof.$(date +%Y%m%d%H%M)
cp /var/lib/redis/dump.rdb /backup/dump.rdb.$(date +%Y%m%d%H%M) 2>/dev/null

# 第 2 步:修复 AOF(切掉文件中第一个不合法的位置之后的所有内容)
redis-check-aof --fix /var/lib/redis/appendonly.aof
# 输出:Successfully truncated AOF at offset 12345678
# 含义:12345678 之后的命令全部丢弃 ------ 丢了多少心里有数

# 第 3 步:校验 RDB(如果同时开了 RDB)
redis-check-rdb /var/lib/redis/dump.rdb

# 第 4 步:启动 + 数据完整度核对
redis-server /etc/redis/redis.conf
redis-cli INFO persistence   # 确认 aof_last_write_status、rdb_last_bgsave_status 正常

决策分支(先想清楚再动手):

现场 决策
AOF 损坏但 RDB 完好、且 RDB 够新 可以走 RDB 启动(关 AOF 或换 RDB 文件),丢数 = AOF 比 RDB 多出的那部分
AOF/RDB 都损坏 fix AOF 后启动,丢尾部;然后从业务对账/日志补数据
有从库且从库数据完整 别修了,直接从库重建(第 31 篇思路反向用)
开了 aof-use-rdb-preamble(混合持久化) --fix 仍能处理,但截断点语义是"RDB 前缀之后的 AOF 部分",修复前确认版本支持

防大于治的两项配置:

conf 复制代码
# redis.conf
aof-load-truncated yes    # 默认 yes:尾巴截断的 AOF 自动忽略并启动(本次的事故就会被自动救回)
no-appendfsync-on-rewrite no   # AOF 重写期间不暂停 fsync(保持默认,别为了性能开它)

注意:aof-load-truncated yes 能救"尾部残缺",救不了"中间损坏"(磁盘坏块)------中间坏了仍要 --fix 或从库重建。

三、Go 实操:抢救后数据完整度核对(收藏点 2)

修复丢的是"文件尾部",核对重点是尾部业务数据的连续性和计数对账:

go 复制代码
package main

import (
	"context"
	"fmt"
	"strconv"

	"redis-0to1/redisx"
)

// 核对思路:业务写入时若用 INCR 维护序号(第 7 篇耐久性实验同款),
// 修复后比对 "最后写入序号" 与业务数据库的最后一条记录。
func main() {
	rdb, _ := redisx.NewClient(redisx.Config{Addr: "127.0.0.1:6379", Password: "redis123", PoolSize: 10})
	ctx := context.Background()

	// 1. 全局序号对账
	seq, err := rdb.Get(ctx, "order:seq").Result()
	if err != nil {
		fmt.Println("序号 key 不存在(丢得比想象多):", err)
		return
	}
	fmt.Println("Redis 恢复到的最后序号:", seq)

	// 2. 抽样验证序号连续性(修复截断点附近的 key 是否齐)
	var n int
	last, _ := strconv.Atoi(seq)
	for i := last - 100; i <= last; i++ {
		exists, _ := rdb.Exists(ctx, fmt.Sprintf("order:%d", i)).Result()
		n += int(exists)
	}
	fmt.Printf("尾部 100 条订单存在 %d 条(缺口=%d,应去数据库回补)\n", n, 100-n)

	// 3. 出报告:缺口区间写入恢复工单
}

产出物:丢数区间报告(截断偏移量 → 业务序号区间)→ 数据库回补 → 下游缓存重建。这一套是"断电事故 SOP"的收尾标准动作。

四、命令清单

命令 作用
redis-check-aof --fix <file> 截断修复 AOF(丢弃不可解析部分)
redis-check-rdb <file> 校验 RDB 完整性
redis-check-aof --truncate-to-timestamp <ts> 按时间点截断(精确保留某时刻前数据)
INFO persistence 启动后核对持久化状态
DEBUG POPULATE / DEBUG JMAP 测试环境制造数据/调试(生产禁用 DEBUG)

五、排查步骤:Redis 拒绝启动

  1. 先看日志最后一行:AOF 格式错误 / RDB 校验失败 / 权限或目录问题,三类原因分开治;
  2. 备份(!)后 redis-check-aof --fix 或换 RDB 启动;
  3. 启动后 INFO persistence + Go 核对脚本验证完整度;
  4. 写事故报告:丢数区间、根因(断电/fsync 策略/磁盘)、整改项;
  5. 整改项至少包含:aof-load-truncated yes 核查、UPS/云盘原子写、从库完整性巡检。

六、总结

  1. AOF 损坏拒启动是保护机制不是 bug:先备份、再 --fix;
  2. --fix 的代价是截断尾部,丢多少必须在修复前评估(RDB 对拍、从库对拍);
  3. aof-load-truncated yes 能自动救尾部残缺,中间损坏靠从库重建;
  4. 抢救的收尾是完整度核对 + 数据库回补,只"启动成功"不算完。

下一篇预告:故障排查弧线最后一案------下一篇《maxclients 打满但 QPS 只有 500------连接数排查 5 步》,一个"幽灵连接大户"的抓捕现场。

相关推荐
csdn_aspnet3 小时前
Redis 入门:Windows、Mac安装指南
数据库·windows·redis·缓存·mac
IT规划师4 小时前
Redis 数据类型全解 - HyperLogLog:12 KB 估出亿级 UV(八)
redis·uv·hyperloglog·.net10
aiks_yuan18 小时前
Token 放 Redis 还是用 JWT:Java 实测验签和查 Redis 耗时,退出登录、改密码踢下线怎么做(jjwt 0.12 + Redis 7.2)
redis
我不会起名字3221 天前
Redis 从 0 到 1(二十九):CPU 突然 100%——commandstats 定位法与 6 步排查链路
redis·cpu·慢查询·排查·commandstats
我不会起名字3221 天前
Redis 从 0 到 1(二十八):Redis 抖一下,重试把它打挂了——超时/重试/熔断的 Go 封装
redis·go·超时·熔断·重试
我不会起名字3221 天前
Redis 从 0 到 1(三十):删了一半数据 RSS 不降——内存碎片与 activedefrag 观测
redis·内存碎片·rss·排查·activedefrag
csdn_aspnet2 天前
使用 Docker 和 RedisInsight 设置 Redis 以进行本地开发
redis·docker·容器·redisinsight
用户094248568032 天前
第22章:Redis 持久化 RDB 与 AOF:数据不丢的底线
redis·架构·源码