bigywater.tech
Redis · 62

AOF 文件损坏修复

AOF 是追加写入的文本,崩溃时可能只写了半条命令,磁盘故障还可能让文件中间坏掉。Redis 遇到这两种情况分别怎么反应?redis-check-aof 到底修了什么?这篇故意把 AOF 文件弄坏,一步一步看启动日志、检查工具的输出,以及修复到底丢了多少数据。

1 个 key
尾部半条命令、aof-load-truncated yes(默认)时丢失的数据
500 / 1000
中间损坏后 redis-check-aof --fix 修复完还剩的 key(丢弃损坏点之后的一切)
20000 ✓ 16500 ✗
aof-load-corrupt-tail-max-size 试过的值:损坏尾部实际 16500 字节

尾部不完整:能自动继续

1000 个 key、appendfsync always;截掉最后 5 字节
实测 · 本机执行输出
incr 文件 appendonly.aof.1.incr.aof:32803 字节
起来了: True  DBSIZE = 1000
实测 · 本机执行输出
aof-load-truncated yes(默认):
  起来了: True  DBSIZE = 999
    Configuration loaded
    Reading RDB base file on AOF loading...
    RDB is base AOF
    Done loading RDB, keys loaded: 0, keys expired: 0.
    DB loaded from base file appendonly.aof.1.base.rdb: 0.000 seconds
    !!! Warning: short read while loading the AOF file appendonly.aof.1.incr.aof!!!
    !!! Truncating the AOF appendonly.aof.1.incr.aof at offset 32770 !!!
    AOF appendonlydir/appendonly.aof.1.incr.aof loaded anyway because aof-load-truncated is enabled
aof-load-truncated no:
  起来了: False  进程退出码: 1
    Configuration loaded
    Reading RDB base file on AOF loading...
    RDB is base AOF
    Done loading RDB, keys loaded: 0, keys expired: 0.
    DB loaded from base file appendonly.aof.1.base.rdb: 0.000 seconds
    Unexpected end of file reading the append only file appendonly.aof.1.incr.aof. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix <filename.manifest>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server.

崩溃时最后一条命令只写了一半是最常见的情形。默认 aof-load-truncated yes:日志出现 short read 警告,Redis 把文件截到最后一条完整命令(offset 32770)继续启动,DBSIZE = 999。改成 no,同一个文件直接拒绝启动,日志提示用 redis-check-aof --fix。(第一次启动时 Redis 已经把文件截断了,所以我在第二次前重新放回了截断的文件。)

中间损坏:一定拒绝启动

把 k500 命令的头 8 个字节改成 GARBAGE!
实测 · 本机执行输出
起来了: False  进程退出码: 1
    Configuration loaded
    Reading RDB base file on AOF loading...
    RDB is base AOF
    Done loading RDB, keys loaded: 0, keys expired: 0.
    DB loaded from base file appendonly.aof.1.base.rdb: 0.000 seconds
    Bad file format reading the append only file appendonlydir/appendonly.aof.1.incr.aof at offset 16303.          make a backup of your AOF file, then use ./redis-check-aof --fix <filename.manifest>.          Alternatively you can set the 'aof-load-corrupt-tail-max-size' configuration option to 16500 and restart the serve

这是格式错误,不是尾部不完整:Redis 报 Bad file format reading the append only file … at offset 16303,退出码 1,不会带着一份不完整的数据启动。

redis-check-aof

实测 · 本机执行输出
   Start checking Multi Part AOF
   Start to check BASE AOF (RDB format).
   RDB preamble is OK, proceeding with AOF tail...
   AOF analyzed: filename=appendonly.aof.1.base.rdb, size=89, ok_up_to=89, ok_up_to_line=1, diff=0
   BASE AOF appendonly.aof.1.base.rdb is valid
   Start to check INCR files.
   0x            3fbc: Expected prefix '$', got: 'G'
   AOF analyzed: filename=appendonly.aof.1.incr.aof, size=32803, ok_up_to=16303, ok_up_to_line=3509, diff=16500
   AOF appendonly.aof.1.incr.aof is not valid. Use the --fix option to try fixing it.

只检查不修改:base 合法(RDB 格式,89 字节);incr 在 0x3fbc 处 Expected prefix '$', got: 'G',ok_up_to=16303,后面 16500 字节都不可信,提示用 --fix。

--fix:丢弃坏点之后的一切

实测 · 本机执行输出
   Start checking Multi Part AOF
   Start to check BASE AOF (RDB format).
   RDB preamble is OK, proceeding with AOF tail...
   AOF analyzed: filename=appendonly.aof.1.base.rdb, size=89, ok_up_to=89, ok_up_to_line=1, diff=0
   BASE AOF appendonly.aof.1.base.rdb is valid
   Start to check INCR files.
   0x            3fbc: Expected prefix '$', got: 'G'
   AOF analyzed: filename=appendonly.aof.1.incr.aof, size=32803, ok_up_to=16303, ok_up_to_line=3509, diff=16500
   This will shrink the AOF appendonly.aof.1.incr.aof from 32803 bytes, with 16500 bytes, to 16303 bytes
   Continue? [y/N]: Successfully truncated AOF appendonly.aof.1.incr.aof
   All AOF files and manifest are valid
修复后 incr 文件 16303 字节(原 32803 字节)
起来了: True  DBSIZE = 500
数据损失:500 个 key(损坏点 k500 及之后的全部命令)

--fix 要求确认,然后把 incr 文件从 32803 字节截到 16303 字节;重启后 DBSIZE = 500。修复的意思是「保留损坏点之前的部分」,损坏点之后的 500 个 key 全部丢失——即使它们本身是完好的。所以动手之前先备份。

Redis 8 的 aof-load-corrupt-tail-max-size

实测 · 本机执行输出
aof-load-corrupt-tail-max-size 1000: 起来了 False,DBSIZE = None,退出码 1
    Bad file format reading the append only file appendonlydir/appendonly.aof.1.incr.aof at offset 16303.          make a backup of your AOF file, then use ./redis-check-aof --fix <filename.manifest>.          Alternatively you can set the 'aof-load-corrupt-tail-max-size' configuration option to 16500 and restart the serve
aof-load-corrupt-tail-max-size 16500: 起来了 False,DBSIZE = None,退出码 1
    Bad file format reading the append only file appendonlydir/appendonly.aof.1.incr.aof at offset 16303.          make a backup of your AOF file, then use ./redis-check-aof --fix <filename.manifest>.          Alternatively you can set the 'aof-load-corrupt-tail-max-size' configuration option to 16500 and restart the serve
aof-load-corrupt-tail-max-size 20000: 起来了 True,DBSIZE = 500,退出码 None
    !!! Warning: corrupt AOF file tail!!!
    !!! Truncating the AOF appendonlydir/appendonly.aof.1.incr.aof at offset 16303 (remaining 16500) !!!
    AOF appendonlydir/appendonly.aof.1.incr.aof loaded anyway because aof-load-corrupt-tail-max-size is enabled

Redis 8 增加了一个选项:允许丢弃损坏点之后最多这么多字节,然后照常启动。试了三个值:1000、16500 拒绝,20000 才通过。损坏尾部实际是 16500 字节(日志 remaining 16500),错误信息也建议设成 16500,但恰好等于它时我这里仍然拒绝——我没有去查为什么(可能是严格的「小于」比较),只能说:设置时要留出余量。这个选项让「丢弃多少」有了上限,不需要人工跑 --fix。

分步演示:三种损坏与三种恢复方式

AOF 损坏与修复
本步涉及的部分

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。损坏是我用脚本直接改 incr 文件造成的;appendfsync always;aof-use-rdb-preamble 使用默认
    • 没有验证:base 文件损坏时的行为、manifest 损坏、损坏发生在多个 incr 文件之一、真实的磁盘位翻转(这里只模拟了两种典型损坏)、aof-load-corrupt-tail-max-size 恰好等于损坏尾部大小时被拒的原因
    京ICP备2026067502号-1京公网安备11010502063487号