首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[MYSQL] 尽可能的从坏块中提取数据 (理论篇)

[MYSQL] 尽可能的从坏块中提取数据 (理论篇)

原创
作者头像
大大刺猬
发布2025-09-28 18:13:54
发布2025-09-28 18:13:54
2170
举报
文章被收录于专栏:大大刺猬大大刺猬

没想到吧, 又讲坏块的恢复, 本章为纯理论, 下一章才讲实现(如果可能的话)

导读

对于"坏块",我们在ibd2sql-1.x版本是使用的try,except来做, 即跳过"有问题的块". 当时为什么这么设计呢?

  1. 这么做最简单.
  2. 坏块的数据可能会干扰正常的数据, 比如坏块部分的数据可能和正常数据有重合,
  3. 坏块部分强制解析出来的结果可能不符合表定义(比如varchar(20)限制是20个字符, 但坏块的情况,可能解析出来超过20个字符)

使用的try和except,未校验crc32c,故一些不相干的位置坏了(比如PAGE_N_DIRECTION), 并不影响数据的解析.

坏块的不可预见性太大了,于是我们就PASS了.

那么ibd2sql-2.x版本怎么做的呢? 忘记这个功能了, 所以来补一下, 顺便看看能不能救一下!

跳过了的那些坏块不要扔, 捡起来裹上面包糠,炸一下, 隔壁dba都馋哭了.

各种坏块

那么, 有哪些位置可能存在损坏呢? 怎么判断哪些部分损坏了呢?

我们先来回顾下FIL_PAGE_INDEX的结构吧.

对象

大小

描述

FIL_HEADER

38

FIL_HEADER

PAGE_HEADER

56

一些页头信息, INDEX PAGE的时候再细看

INFIMUM

13

最小的行(虚拟的)

SUPEREMUM

13

最大的行(虚拟的)

USER RECORD

x

实际的用户数据

FREE

y

未使用的部分

PAGE_DIRECTORY

z

方便页内快速查找数据的. (页内目录)

FIL_TRAILER

8

FIL_TRAILER

哪些位置可能有坏块呢? 都可能坏, 甚至全部都坏了... 坏得甚至认不出来是不是index page, 坏得连TM(parent node)都不认识.....

但并非有坏的就有影响, 比如free部分, 坏就坏了呗, 反正是未使用空间,也没得用户数据

怎么判断哪些部分损坏了呢? 判断不了, 我们只有整个page的校验值, 没有各模块的校验值, 这也是我们之前不解析坏块的原因, 比较都不知道哪坏了....

FIL_HEADER

fil_header是每个page都有的, 也就是记录一些基础信息, 基本上影响不大,大不了我对于坏块全部强制解析呗. 格式和影响范围如下:

对象

大小(字节)

描述

影响范围

FIL_PAGE_SPACE_OR_CHECKSUM

4

老版本(4.0)是space id,新版本是checksum, 我们就只把它当作checksum

根据这个来校验是否有坏块的, 所以坏了只影响校验,不影响数据

FIL_PAGE_OFFSET

4

这个PAGE在space的偏移量, 就是我们说的PAGE ID

记录当前page属于space的哪个page, 如果是index page,则没有影响; 如果是blob相关的,则看数据文件是否完整, 若完整,则不影响(是通过f.seek定位pageid的), 若不完整(碎片页), 则有影响

FIL_PAGE_PREV/FIL_PAGE_SRV_VERSION

4

上一页在哪? 对于index page(btr+),为了方便查找前后页,故存储了前后页的PAGEID. (如果是page0 则为mysql版本)

不影响, 我们是一页页强制解析的.

FIL_PAGE_NEXT/FIL_PAGE_SPACE_VERSION

4

记录下一页的在这呢. (如果是page0 则为mysql版本, 实际上是1.)

不影响

FIL_PAGE_LSN

8

LSN值

不影响

FIL_PAGE_TYPE

2

page的类型. (如果是压缩页, 这之后的信息就得压缩了)

有一定影响, 毕竟都不知道这一页是哪种类型了. 如果是未知页, 则可以强制解析, 但如果变成了已知类型的页, 则可能出现未知错误, 毕竟我们只知道是否坏块,并不知道哪坏了

FIL_PAGE_FILE_FLUSH_LSN

8

只有第1页才有的, 表示之前的LSN都落盘了, 实际上是0.

不影响

FIL_PAGE_SPACE_ID (FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID)

4

表空间id

不影响, 通常我们使用indexid来区分是否是同一个btr+.

PAGE_HEADER

page_header是FIL_PAGE_INDEX和FIL_PAGE_SDI才有的, 记录一些索引相关的信息, 比如indexid, 很多都是和查询有关的,影响不大. 结构和影响范围如下:

对象

大小

描述

影响范围

PAGE_N_DIR_SLOTS

2

记录PAGE_DIRECTORY的数量的

每行数据会记录下一行数据的未知, 所以只是PAGE_N_DIR_SLOTS坏了, 影响不大.

PAGE_HEAP_TOP

2

pointer to record heap top, 就是最大的heap 可以简单理解为:这之后的空间未使用

不影响

PAGE_N_HEAP

2

第15bit表示是否为new-style,剩余的表示(PAGE_N_HEAP&32767):本页记录的数据量(含INFIMUM和SUPEREMUM和被标记为delete的行)

不影响, 我们全部解析

PAGE_FREE

2

指向第一个被标记为delete的行, 没得的话就是0

不影响, 通常我们是要未删除的数据. 刚删除了数据,又发生了坏块,还没的备份和日志,这种是概率是比较小的.

PAGE_GARBAGE

2

有多少空间可以回收(就是PAGE_FREE有多少字节)

不影响

PAGE_LAST_INSERT

2

最后一次insert的位置, 如果是delete的话,就为0 (如果update更新再原来的位置不够,PAGE_HEAP_TOP后面也有足够空间的话, 也会更新这个值.)

不影响

PAGE_DIRECTION

2

last insert direction (最后一条记录插入方向).PAGE_LEFT = 1;PAGE_RIGHT = 2;PAGE_SAME_REC = 3;PAGE_SAME_PAGE = 4;PAGE_NO_DIRECTION = 5

不影响

PAGE_N_DIRECTION

2

同一个方向插入的数量, 方向变化后,就重置为0 (比如一直递增insert的话, 这个值大概率就是页内字段数量-1)

不影响

PAGE_N_RECS

2

页内的数据行数(不含INFIMUM和SUPEREMUM和被标记为delete的行)

不影响

PAGE_MAX_TRX_ID

8

最大事务ID(二级索引(叶子节点)和insert buffer才有的)

不影响

PAGE_LEVEL

2

当前page位于btr+的哪一层(0表示叶子节点, root page是最高层)

影响不大, 毕竟rec_header也记录当前页是否为叶子节点.

PAGE_INDEX_ID

8

这页属于哪个索引.

如果是碎片页,则影响. 否则不影响.

PAGE_BTR_SEG_LEAF

4+4+2

只有root page才有, 记录本索引对应的segment的spaceid,pageno,offset (leaf page信息)

不影响

PAGE_BTR_SEG_TOP

4+4+2

只有root page才有, 记录本索引对应的segment的spaceid,pageno,offset (root page信息)

不影响

PAGE_DIRECTORY 方便页内快速查找数据的, 通常4-8行数据记录一下(使用2字节记录的是页内偏移量) PAGE_BTR_SEG_TOP和PAGE_BTR_SEG_LEAF通常相差192(一个segment大小), 8.0主键通常是434(38+12+1922(sdi的))和626(38+12+1922+192)

INFIMUM & SUPEREMUM

我们是根据INFIMUM来遍历页内数据行的, 如果INFIMUM坏了, 我们还可以根据PAGE_DIRECTORY来判断, 问题是我们并不知道究竟是哪坏了?

USER RECORD

影响全部/部分页内数据. 就相当于某部分REC掉链子了. 我们还可以根据PAGE_DIRECTORY来辅助判断.

FREE

坏就坏了吧, 反正我也发现不了它.

PAGE_DIRECTORY

如果user record正常, 则PAGE_DIRECTORY损坏没有影响. 否则会丢某部分数据.

FIL_TRAILER

对象

大小

描述

影响范围

CHECKSUM

4

校验值(CHECKSUM = FIL_PAGE_SPACE_OR_CHECKSUM)

影响校验

FIL_PAGE_LSN

4

LSN(低4字节, 即FIL_PAGE_LSN = FIL_PAGE_LSN & 0xffffffff)

不影响

怎么恢复坏块中的数据呢?

我们都没法判断坏在哪了, 那么应该怎么恢复坏块中的数据呢?

我们要把坏块当作每个部分都坏了, 所以

  1. 我们得强制解析坏块的PAGE.
  2. 遍历page directory和rec统计所有的rec offset并去重.
  3. 根据rec offset解析前4000行数据(限制每页的4000行数据是防止无限循环下去), 均当作primary key的叶子节点来解析.
  4. 筛选不符合表定义的行,并输出符合要求的数据行.

初步打算这么设计, 但未作实际验证, 目前只是处于理论阶段. .

工具设计

对于ibd2sql工具使用的设计, 我们要不影响原来的使用方法的前提下, 尽可能的方便使用者使用. 故

  1. 如果没有--log--output,则坏块信息输出到stderr
  2. 如果有--log,但没有--output,则输出到stdout
  3. 如果有--output,则输出文件以.warn结尾
  4. --set bad-pages=force 为启用坏块的解析, 当启用时:自动添加--force,--set roono=0, --set leafno=0选项

总结

先简单记录下相关原理,免得后面忘记了,万一实现不了的话, 就try吧-_-

代码未动,理论先行!

若各位大佬有更好的建议请随时沟通.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导读
  • 各种坏块
    • FIL_HEADER
    • PAGE_HEADER
    • INFIMUM & SUPEREMUM
    • USER RECORD
    • FREE
    • PAGE_DIRECTORY
    • FIL_TRAILER
  • 怎么恢复坏块中的数据呢?
  • 工具设计
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档