bigywater.tech
Redis · 63

RDB 文件校验

RDB 文件被磁盘、拷贝或网络传输弄坏一个字节,Redis 能发现吗?靠的是文件末尾那个 8 字节的 CRC64 校验和,以及 redis-check-rdb 这个工具。这篇故意弄坏 RDB 文件三种方式,看 Redis 与检查工具各自的反应,并量一下打开校验和要多花多少时间。

CRC64
文件末尾 8 字节,整个 RDB 的校验和(rdbchecksum yes 时)
静默损坏
rdbchecksum no 时改一个字节:Redis 正常启动,GET 返回被改坏的值
约 7%
100 万个 key 的 SAVE:0.301 s(yes)vs 0.281 s(no)

健康的文件

实测 · 本机执行输出
dump.rdb:5488988 字节;结尾 8 字节(CRC64)= 1b8749a5723c6a4a
退出码 0
   [offset 0] Checking RDB file <实验目录>
   [offset 82] Selecting DB ID 0
   [offset 5488988] Checksum OK
   [offset 5488988] \o/ RDB looks OK! \o/
   [info] 200000 keys read
   [info] 0 expires
   [info] 0 already expired
   [info] 0 subexpires

20 万个 key 的 dump.rdb 有 5488988 字节,末尾 8 字节是校验和。redis-check-rdb 依次读完整个文件,报告 Checksum OK 和读到的 key 数。

改一个字节:被校验和拦下

实测 · 本机执行输出
起来了: False  退出码: 1
    RDB age 0 seconds
    RDB memory usage when created 12.97 Mb
    Wrong RDB checksum expected: (e2a2654ddb185661) but got (4a6a3c72a549871b). Aborting now.
    Internal error in RDB reading offset 5242869, function at rdb.c:5072 -> RDB CRC error
redis-check-rdb:
   [offset 0] Checking RDB file <实验目录>
   [offset 82] Selecting DB ID 0
   --- RDB ERROR DETECTED ---
   [offset 5488988] RDB CRC error
   [additional info] While doing: check-sum
   [info] 200000 keys read
   [info] 0 expires
   [info] 0 already expired
   [info] 0 subexpires

我用脚本找到 value-00100000 并把其中一个字符改成 X,再启动 Redis:它读完整个文件才发现校验和对不上(Wrong RDB checksum expected … but got … Aborting now),退出码 1,拒绝启动。redis-check-rdb 的诊断更直白:RDB CRC error,位置 5488988 就是文件末尾——校验和是在读完之后才比较的,所以它能告诉你「文件坏了」,不能告诉你「坏在哪一个 key」。

关掉校验和:静默损坏

实测 · 本机执行输出
结尾 8 字节 = 0000000000000000(全 0 = 未计算校验和)
起来了: True  DBSIZE = 200000
GET user:100000 → value-00100X00   (原值 value-00100000)

rdbchecksum no 生成的文件末尾 8 字节是 0。同样改一个字节,Redis 正常启动,DBSIZE = 200000,GET user:100000 返回被改坏的 value-00100X00。这就是 rdbchecksum no 的风险:磁盘或传输中的位翻转不会被发现——它省下的只是校验和计算的时间(见下面)。

截断:不需要校验和也能发现

实测 · 本机执行输出
起来了: False  退出码: 1
    RDB age 1 seconds
    RDB memory usage when created 12.97 Mb
    Internal error in RDB reading offset 2621439, function at rdb.c:5096 -> Unexpected EOF reading RDB file
redis-check-rdb:
   [offset 0] Checking RDB file <实验目录>
   [offset 82] Selecting DB ID 0
   --- RDB ERROR DETECTED ---
   [offset 2744483] Unexpected EOF reading RDB file
   [additional info] While doing: read-object-value
   [additional info] Reading key 'user:196149'
   [additional info] Reading type 0 (string)
   [info] 99996 keys read

只剩前一半的文件:Redis 报 Unexpected EOF reading RDB file;redis-check-rdb 指出出错位置、正在读的 key 和类型。结构性损坏(截断、格式错误)不依赖校验和。

校验和的代价

实测 · 本机执行输出
rdbchecksum yes:[0.328, 0.301, 0.312, 0.3, 0.296]  中位 0.301 s
rdbchecksum no :[0.286, 0.28, 0.281, 0.278, 0.298]  中位 0.281 s

100 万个 key、关闭压缩的 SAVE,每种配置连续 5 次取中位:有校验和 0.301 s,无校验和 0.281 s,差约 20 ms(约 7%)。这是本机一份小数据集(key 与 value 都很短)上的数字,数据集里 value 更大时占比会不同。为这点时间关掉校验和,通常不划算。

分步演示:三种损坏

RDB 文件校验
本步涉及的部分

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。实验里关掉了 rdbcompression,这样 value 在文件中是明文,方便定位并修改一个字节
    • 没有验证:压缩(LZF)开启时的损坏表现、diskless 复制传输中的校验、RDB 版本兼容、redis-check-rdb 对 8.x 新数据类型的支持范围、数据集更大时校验和的时间占比
    京ICP备2026067502号-1京公网安备11010502063487号