innodb_ruby可精准解析.ibd文件页结构,需Ruby环境安装并确保独立表空间;先用innodb_space定位数据页,再解析记录,注意COMPACT行格式及页头、槽、堆布局规则。

怎么用 innodb_ruby 看懂一个 .ibd 文件里的页结构
直接看二进制数据不可能人工识别,得靠工具把 16KB 页拆解成可读的字段。最靠谱的是 innodb_ruby,它能精确还原 InnoDB 的页头、记录堆、槽(slot)、页尾等真实布局。
实操建议:
- 用
gem install innodb_ruby安装(需 Ruby 环境),别用 pip 装错包 - 确保目标表是独立表空间(
innodb_file_per_table=ON),否则.ibd文件混着多个表,解析会错乱 - 先用
innodb_space -f t.ibd space-page-type-regions确认哪些页是数据页(B-tree node),再针对某一页解析,比如innodb_space -f t.ibd page-records 3 - 注意:MySQL 5.7+ 默认用
COMPACT行格式,innodb_ruby对DYNAMIC和COMPRESSED支持有限,解析可能丢字段或报错
为什么 hexdump 看到的前 38 字节不是“随便一堆数”
那其实是页头(FIL Header)和页描述(Page Header)的固定区域,每个字节都有定义,改错一个就导致页损坏无法加载。
关键字段位置和含义:
- 偏移 0–7:
FIL_PAGE_SPACE_OR_CHKSUM,MySQL 8.0 后是校验和,之前是 space id - 偏移 8–9:
FIL_PAGE_OFFSET,该页在表空间内的逻辑页号(比如第 3 页就是 0x0003) - 偏移 24–25:
PAGE_N_DIR_SLOTS,槽的数量,等于页内记录数(含删除标记)向上取整到 8 的倍数 - 偏移 34–35:
PAGE_HEAP_TOP,空闲空间起始偏移,超过这个值就是“未使用区域”,但不等于页尾
常见错误现象:手动修改 .ibd 时只改了记录内容,没同步更新 PAGE_N_RECS 或 PAGE_HEAP_TOP,MySQL 启动后报错 Database page corruption on disk。
记录(record)在页里怎么排列?为什么不能按顺序读内存
InnoDB 不把记录从头到尾线性存,而是分三块:记录堆(heap)、槽数组(directory slots)、页尾(FIL Trailer)。真正查一条记录,得先查槽,再跳转到堆中实际位置。
原因和影响:
- 槽数组倒序存放,
slot[0]指向最后插入的记录,slot[n-1]指向第一条记录 —— 这是为了支持页内记录移动(如 update 变长字段)时只改槽,不动记录本体 - 每条记录开头有 5 字节头部(
record header),含 deleted flag、n_owned、heap_no 等,必须结合槽索引才能定位有效记录 - 如果你用
hexdump -C t.ibd | head -n 200直接扫,会看到大量00 00 00 00和重复字段,那是空闲区或已删除记录残留,不是“数据缺失” - 行格式影响布局:
COMPACT把 NULL 值用 bitmap 存在记录头后;DYNAMIC则把大字段(如 TEXT)外移到溢出页,主页只留 20 字节指针
page directory(槽数组)大小怎么算?为什么经常看到 0x0008 却只有 6 条记录
槽数量不是记录数,而是记录数向上对齐到 8 的倍数再除以 8,再加 1 —— 这个公式来自 InnoDB 源码里的 page_dir_calc_slot_cnt 函数。
举个例子:
- 6 条记录 → ceil(6 / 8) = 1 → 槽数 = 1 + 1 = 2?不对。实际是:每 4~8 条记录分配一个槽,所以 6 条记录会分到 2 个槽组,共
PAGE_N_DIR_SLOTS = 2(即 0x0002),不是 0x0008 - 0x0008 常见于刚建表、插入 8 条记录后未触发页分裂的状态,此时刚好填满一个槽组
- 槽本身占 2 字节/个,存在页尾往前的位置(紧挨
FIL Trailer),总数写在页头偏移 24 处,不读这里就找不到槽在哪 - 误判槽数会导致解析工具把记录头当槽、把槽当记录,结果整个页结构全错
真正要确认一条记录是否有效,得同时检查它的 record header 中的 deleted flag 和所在槽指向的地址是否落在当前页的 heap 区域内 —— 这两步缺一不可。










