Redis AOF文件需用二进制模式逐字节解析RESP协议,不能用std::getline;须跳过#注释行,处理MULTI/EXEC事务状态,正确解析嵌套结构与NULL,并防范截断、溢出及换行符转换问题。

Redis AOF 文件不是标准协议文本,直接用 std::getline 读取会断在错误位置
Redis 的 AOF 文件本质是按 RESP(REdis Serialization Protocol)格式序列化的命令流,但不是纯文本行——它包含二进制长度前缀、嵌套结构和可能的空字节。用 std::getline 按 \n 切割,大概率在 $5\r\nHELLO\r\n 的中间截断,导致解析失败。
正确做法是逐字节解析 RESP 结构:先读首字节判断类型(+ 简单字符串、$ 批量字符串、* 数组等),再按后续数字解析长度,最后读指定字节数(含 \r\n)。必须自己实现 RESP 解析器,不能依赖行缓冲。
- 遇到
$-1表示 NULL,不是空字符串,需特殊处理 - AOF 中的
SELECT、EXPIREAT等命令可能带整数参数,注意std::stoi溢出(Redis 用 int64_t,C++ 需用std::stoll) - 部分 AOF 文件开头有 Redis 版本注释(如
# REDIS VERSION 7.2.5),需跳过以#开头的整行,但不能跳过命令中的注释参数(极少见)
用 std::ifstream 以二进制模式打开,禁用 locale 和缓冲区自动换行转换
Windows 下若用文本模式打开 AOF 文件,\r\n 会被转成单个 \n,破坏 RESP 的严格格式;locale 设置(如中文环境)可能导致 std::cin 对非 ASCII 字节解码异常。必须显式指定:
std::ifstream file("appendonly.aof", std::ios::binary);
file.unsetf(std::ios::skipws); // 关键:禁用跳过空白符
- 不要用
file >>提取操作符,它依赖空格分隔,而 RESP 中无空格分隔符 - 避免
file.get()单字节读取(太慢),改用file.read(buf, n)批量读,但需手动校验是否读满 - 读到 EOF 时,
file.eof()只在尝试读失败后置位,应检查file.gcount() == n
multi/exec 块和 discard 会影响命令上下文,需维护事务状态栈
AOF 不仅记录单条命令,还记录事务块。一个 *2\r\n\r\nMULTI\r\n 后面紧跟的若干命令属于同一事务,直到遇到 *1\r\n\r\nEXEC\r\n 或 *1\r\n\r\nDISCARD\r\n。解析器若忽略这个状态,会把 INCR 当作顶层命令执行,丢失原子性语义。
- 维护一个
int transaction_depth,遇到MULTI加 1,EXEC/DISCARD减 1 - 深度 > 0 时,所有命令应暂存为待提交队列,而非立即处理
- 注意嵌套事务(Redis 实际不支持,但 AOF 文件里若出现
MULTI在未EXEC前又来一个,是非法 AOF,应报错退出) -
WATCH命令只影响后续EXEC是否成功,AOF 中它只是普通命令,无需特殊解析,但需保留其存在
忽略 redis-check-aof --fix 生成的修复标记,它们不是合法 RESP
当 AOF 损坏后,redis-check-aof --fix 可能插入类似 # Warning: 'xxx' is not a valid command, skipping... 这类注释行。这些行以 # 开头但不符合 RESP 格式,且不在 Redis 协议规范内。
- 解析循环中,若读到
#,必须读完整行(直到下一个\n或 EOF),然后丢弃,不能当作命令处理 - 不能假设
#行一定以\r\n结尾——修复工具在不同系统下可能只写\n - 更稳妥的做法:在每次解析新命令前,循环跳过所有以
#开头的行,且兼容\r\n和\n
真正难的不是读取字节,而是 RESP 的递归结构:一个数组命令(*)里每个元素可能是字符串、另一个数组,甚至 null。C++ 没有内置的动态协议树,你得决定是构建内存 AST 节点,还是边解析边回调处理——后者省内存,但无法回溯;前者灵活,但容易因 AOF 截断导致内存泄漏。别忘了 AOF 可能被截断(如突然断电),解析器必须能识别不完整 RESP 并安全退出。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











