Redis 6.0+ RDB中哈希对象编码由RDB版本和length encoding共同决定:7.0+默认用listpack(需按偏移遍历解码LP_ENCODING),否则可能为dict(须严格按rdbLoadLen→rdbLoadRawString→跳padding解析);解析前必须校验rdbver、EOF及length溢出。

Redis RDB 哈希对象的编码类型怎么判断
Redis 6.0+ 的 RDB 文件中,哈希(hash)对象可能用两种编码:REDIS_RDB_TYPE_HASH(老式 ziplist 或 hashtable)或更常见的 REDIS_RDB_TYPE_HASH_ZIPMAP 已被彻底移除;实际遇到的几乎全是 REDIS_RDB_TYPE_HASH,但其内部结构取决于写入时的配置和数据规模。关键不是“是什么类型”,而是 RDB 解析器读到该 type 后,要按「键值对数量 + 编码标志」决定后续解析路径。
真正需要检查的是 RDB 的 version 字段(在文件头),以及紧随 type byte 后的 length encoding —— Redis 7.0 开始默认启用 listpack 替代 ziplist,所以哈希底层可能是 listpack 或 dict。解析前必须先读 rdbLoadLen 得到元素对数,再根据 RDB version 和 encoding flag 判断下一段是 listpack blob 还是连续的 key-value string pairs。
如何正确读取哈希的 listpack 编码结构
listpack 是 Redis 6.2 引入、7.0 成为哈希默认编码的紧凑二进制格式,它不像 ziplist 那样有 entry header 长度字段嵌套,而是靠偏移量跳转。C++ 解析时不能硬解 offset 数组,必须逐 entry 遍历:
- 先读 4 字节 total bytes,再读 2 字节 num_entries(注意大小端,RDB 是小端)
- 从末尾往前读每个 entry 的长度(listpack 使用反向长度表),用
lpGet-style 手动跳过 header 和 prevlen 字段 - 每个 key 和 value 在 listpack 中交替存放,且都以
LP_ENCODING开头(1~5 字节),需先 decode encoding type 再读 content
常见错误是把 listpack 当作纯字符串流直接 memcpy,结果在遇到 LP_ENCODING_INT(如 0x01 表示 int8)时解析出乱码 key。必须用类似 Redis 源码中 lpGetValue 的逻辑分支处理每种 encoding。
立即学习“C++免费学习笔记(深入)”;
解析 dict 编码哈希时为何总少读一个 key-value 对
当 RDB 中哈希对象 size 超过 hash-max-listpack-entries(默认 512)或任意 value 长度超过 hash-max-listpack-value(默认 64),Redis 会 fallback 到 dict 编码。此时 RDB 格式是:先写 num_entries * 2 个 string objects(交替 key/value),但没有显式分隔符。
容易漏掉一对的根本原因是:RDB 的 string object 本身带长度前缀(rdbLoadStringObject 流程),而很多 C++ 实现误以为「读完一个 string 就自动对齐到下一个」,忽略了 string 内部可能含 \0、且长度前缀可能是 1/2/5 字节变长编码。必须严格按 rdbLoadLen → rdbLoadRawString → 跳过 padding 的三步走,否则第 n 个 value 的起始位置会偏移。
另外注意:Redis 7.0+ 的 RDB 可能启用 LZF 压缩(REDIS_RDB_ENC_LZF),若未检测并解压就直接 parse,会导致整个 dict 结构错位——这个 flag 出现在每个 string object 的 type byte 里,不是全局开关。
用 C++ 读 RDB 哈希时最该检查的三个边界条件
RDB 是二进制协议,没有校验和(除了可选的 CRC64 footer),出错不会报错,只会静默错位。以下三点不验证,90% 的解析结果不可信:
-
rdbver:必须在解析任何对象前确认版本号(REDIS_RDB_VERSION定义在src/rdb.h),不同版本的 listpack 结构、LZF 标志位置、甚至哈希的 entry count 编码方式都不同 - EOF 检查:每次调用
fread或read后必须检查返回值是否等于预期字节数,RDB 不保证文件末尾对齐,少读 1 字节就会让后续所有 type 判断失效 - length decoding 溢出:
rdbLoadLen返回的长度可能极大(比如被恶意构造),C++ 中若用uint32_t接收却未检查是否超限,后续malloc会触发整数溢出或分配失败
真正难的不是解析单个哈希,而是让整个 RDB 流在任意损坏、截断、版本混杂的现场下仍能定位到下一个合法 REDIS_RDB_TYPE_HASH —— 这要求你把 RDB 当作状态机来写,而不是线性读取器。



















