1. 首页 > 软件下载 > 正文

MySQL数据库恢复工具FGMDU(FGEDU MySQL DUL)

**FGMDU**(全称 **FGEDU MySQL Data UnLoader**)是一款由 fgedu 研发的MySQL / MariaDB / Percona 数据灾难恢复工具,当 MySQL / MariaDB / Percona Server 因损坏、误操作或系统故障无法正常启动时,FGMDU 可以直接读取 InnoDB / MyISAM 的底层存储文件,解析其页面结构与记录格式,把数据抽取为可导入的 DMP 或 SQL 文件用于恢复。
> 本手册为FGMDU完整说明文档,涵盖程序介绍、功能特性、使用方法、典型案例、问题排查等全部内容。

## 目录

1. [程序介绍](#1-程序介绍)
– 1.1 [项目概述](#11-项目概述)
– 1.2 [作者信息](#12-作者信息)
– 1.3 [设计理念](#13-设计理念)
– 1.4 [适用场景](#14-适用场景)
2. [功能特性](#2-功能特性)
– 2.1 [核心功能](#21-核心功能)
– 2.2 [存储引擎支持](#22-存储引擎支持)
– 2.3 [数据字典支持](#23-数据字典支持)
– 2.4 [输出格式](#24-输出格式)
– 2.5 [恢复能力详解](#25-恢复能力详解)
– 2.6 [稳定性与安全加固](#26-稳定性与安全加固)
– 2.7 [跨平台能力](#27-跨平台能力)
3. [支持环境](#3-支持环境)
– 3.1 [支持的数据库版本](#31-支持的数据库版本)
– 3.2 [支持的操作系统](#32-支持的操作系统)
– 3.3 [编译环境要求](#33-编译环境要求)
– 3.4 [运行环境要求](#34-运行环境要求)
4. [程序使用](#4-程序使用)
– 4.1 [编译与安装](#41-编译与安装)
– 4.2 [命令一览](#42-命令一览)
– 4.3 [通用选项](#43-通用选项)
– 4.4 [unload 命令 – 数据抽取](#44-unload-命令—数据抽取)
– 4.5 [scan 命令 – 页面结构扫描](#45-scan-命令—页面结构扫描)
– 4.6 [desc 命令 – 表结构描述](#46-desc-命令—表结构描述)
– 4.7 [recover 命令 – 灾难恢复](#47-recover-命令—灾难恢复)
– 4.8 [坏块跳过与部分恢复](#48-坏块跳过与部分恢复)
– 4.9 [按用户名/数据库/对象名过滤](#49-按用户名数据库对象名过滤)
– 4.10 [输出格式说明](#410-输出格式说明)
5. [程序各种案例场景与操作过程](#5-程序各种案例场景与操作过程)
6. [常用问题与排查](#6-常用问题与排查)

## 1. 程序介绍

### 1.1 项目概述

**FGMDU**(全称 **FGEDU MySQL Data UnLoader**)是一款由 fgedu 研发的 MySQL 数据灾难恢复工具,当 MySQL / MariaDB / Percona Server 因损坏、误操作或系统故障无法正常启动时,FGMDU 可以直接读取 InnoDB / MyISAM 的底层存储文件,解析其页面结构与记录格式,把数据抽取为可导入的 DMP 或 SQL 文件用于恢复。

不同于依赖数据库实例在线运行的逻辑备份工具(如 mysqldump、mysqlpump),FGMDU 工作在文件系统层级,不依赖任何数据库进程。这意味着即使数据库服务彻底崩溃、ibdata1 损坏、ib_logfile 丢失、配置文件错误导致无法启动,只要底层 `.ibd` / `.MYD` / `ibdata1` 文件仍然可读,就有机会把数据抢救出来。

FGMDU 使用纯 C 语言编写,遵循 C99 标准,通过 `src/common/portable.h` 抽象层处理 Linux 与 Windows 平台差异,单一代码库即可在两个平台编译运行。工具不依赖任何第三方库(无 MySQL 客户端库、无 Boost、无 OpenSSL),仅使用标准 C 库与操作系统原生 API,因此可以静态编译后直接拷贝到目标机器运行,非常适合无网络、无包管理器的灾后恢复现场。

### 1.2 作者信息

– **项目名称**:FGMDU(fgedu mysql dul)
– **当前版本**:1.0
– **作者 / 维护方**:风哥 fgedu
– **项目定位**:MySQL / MariaDB / Percona 数据灾难恢复工具
– **命名含义**:
– **FG** = fgedu(作者组织标识)
– **目标操作系统**:Linux RHEL 5/6/7/8/9/10,Windows 7/8/8.1/10/11,Windows Server 2008 R2 / 2012 / 2016 / 2019 / 2022
– **支持数据库**:MySQL 5.6 / 5.7 / 8.0 / 8.4 / 9.7,MariaDB 全系列,Percona Server 全系列
## 关于作者
| 联系方式 | 信息 |
|———|——|
| **作者** 风哥
| **WX**  itpux-com
| **QQ**  113257174
| **官方网站** : http://www.fgedu.net.cn , http://www.itpux.com
| **数据库教程** : https://edu.51cto.com/lecturer/8020378.html

### 1.3 设计理念

FGMDU 在设计上遵循以下原则:

1. **绕过实例,直读文件**:不启动 mysqld,不连接 SQL 接口,直接以只读方式打开数据文件,避免对原始数据造成二次破坏。
2. **零外部依赖**:单一代码库、标准 C99、无第三方库,可静态编译后单文件部署到无网络环境。
3. **跨平台一致体验**:Linux 与 Windows 共享同一份源代码,命令行参数、输出格式、行为语义完全一致。
4. **渐进式恢复策略**:从”正常抽取”到”已删除记录恢复”再到”坏块启发式恢复”和”无字典暴力扫描”,多级递进,先易后难,最大程度抢救数据。
5. **安全优先**:所有 I/O 操作只读,绝不修改源文件;所有内存分配带 OOM 处理;所有指针与边界检查齐全;遇到坏块自动降级而非崩溃退出。
6. **详细进度反馈**:抽取过程中实时输出每张表的表名、抽取行数、扫描进度,便于用户在大表恢复时监控状态。
7. **命令简洁清晰**:使用 `unload / scan / desc / recover / version / help` 等完整英文单词作为主命令名,避免缩写歧义。

### 1.4 适用场景

FGMDU 适用于但不限于以下场景:

– **数据库无法启动**:my.cnf 配置错误、ib_logfile 损坏、系统表空间崩溃、权限问题、依赖库缺失等导致 mysqld 无法启动,但数据文件完好。
– **单表损坏**:某张表的 `.ibd` 文件页面损坏,MySQL 报错无法读取该表,但其他表正常。
– **误删数据恢复**:误执行 `DELETE FROM` 删除了重要记录,被删数据仍残留在页面空闲链中尚未被覆盖。
– **误删表恢复**:误执行 `DROP TABLE` 后,在 file-per-table 模式下原 `.ibd` 文件被删除,需要先在文件系统层面恢复 `.ibd` 再用 FGMDU 抽取。
– **TRUNCATE 后恢复**:`TRUNCATE TABLE` 重建了表文件,原数据页面可能残留在旧文件中。
– **坏块磁盘恢复**:存储介质出现物理坏块(EIO 错误),需要尽可能抢救坏块周围的可读数据。
– **共享主机多用户恢复**:不同用户的 MySQL 数据分布在不同用户目录,需要按用户名批量恢复并交还。
– **跨平台恢复**:Windows MySQL 崩溃,将 `.ibd` 复制到 Linux 恢复服务器用 FGMDU 处理。
– **无数据字典恢复**:DDL、SDI、`.frm` 文件全部丢失,仅有 `.ibd` 文件,使用原始暴力扫描模式提取记录字节。
– **ibdata1 共享表空间恢复**:使用 `innodb_file_per_table=OFF` 模式,所有表数据在 ibdata1 中,需要按表名过滤提取。

 

## 2. 功能特性

### 2.1 核心功能

FGMDU 提供四大核心命令,覆盖从正常抽取到灾难恢复的完整链路:

| 命令 | 用途 | 典型场景 |
|——|——|———|
| `unload` | 正常数据抽取 | 数据库无法启动但文件完好 |
| `scan` | 页面结构分析 | 诊断文件损坏程度、评估恢复可行性 |
| `desc` | 查看表结构 | 确认数据字典可用性、查看列定义 |
| `recover` | 灾难恢复 | 文件损坏、误删数据、坏块磁盘恢复 |

**功能清单:**

– 从 `.ibd` 文件(file-per-table 模式)提取数据
– 从 `ibdata1`(共享表空间)按表名过滤提取数据
– 从 `.MYD` / `.MYI` 文件提取 MyISAM 表数据
– 恢复已删除记录(DELETE 后残留数据)
– 恢复损坏页面中的数据(FIL 头损坏时的启发式恢复)
– 恢复 BLOB / TEXT 外部页面数据
– 无数据字典情况下的原始数据暴力扫描
– 按数据库名、用户名、对象名过滤恢复
– 递归扫描整个 MySQL 数据目录
– 按数据库名自动生成子目录输出
– 支持大文件(>2GB)通过 LFS 处理
– 坏块自动跳过与部分恢复
– 详细进度与每表抽取行数报告

### 2.2 存储引擎支持

| 引擎 | 支持状态 | 说明 |
|——|———|——|
| InnoDB | 完全支持 | COMPACT/DYNAMIC 行格式,file-per-table 和共享表空间 |
| MyISAM | 支持 | 静态/动态行格式 |
| MEMORY | 不支持 | 数据在内存中,无持久化文件 |
| NDB Cluster | 不支持 | 分布式存储,非本地文件 |

### 2.3 数据字典支持

FGMDU 支持多种数据字典来源,按优先级自动尝试:

| 字典模式 | 选项值 | 说明 |
|———|——–|——|
| 自动 | `–dict auto` | 按以下顺序依次尝试(默认) |
| InnoDB 系统字典 | `–dict innodb-sys` | 5.6/5.7 的 SYS_TABLES/SYS_COLUMNS |
| SDI 序列化字典 | `–dict sdi` | MySQL 8.0+ 的 `.sdi` JSON 文件 |
| .frm 旧式字典 | `–dict frm` | MySQL 5.7 及以前的 `.frm` 文件 |
| 无字典原始扫描 | `–dict raw` | 不依赖字典,暴力扫描 INDEX 页面 |

字典加载顺序(`–dict auto` 模式):
1. 显式 DDL 文件(`–ddl` 指定的单表 DDL)
2. DDL 目录(`–ddl-dir` 下的 `<table>.sql`)
3. SDI 目录(`–sdi-dir` 下的 `.sdi` JSON)
4. 数据目录同位置的 `.frm` 文件
5. 数据目录同位置的 `<table>.sql` 文件

### 2.4 输出格式

| 格式 | 选项值 | 说明 |
|——|——–|——|
| DMP | `–format dmp` | FGMDU 自定义二进制格式,每表一个 `.dmp` 文件(默认) |
| SQL | `–format sql` | 标准 SQL INSERT 语句,支持事务批量提交 |
| 两者 | `–format both` | 同时生成 `.dmp` 与 `.sql` |

– **输出目录结构**:
– 默认(扁平):`<outdir>/<database>.<table>.dmp`
– `–struct-outdir`(分层):`<outdir>/<database>/<table>.dmp`
– **SQL 批量提交**:默认每 1000 行 `COMMIT` 一次,可通过 `–rows-per-txn` 调整
– **恢复输出**:`recover` 命令输出为 `recovered_data.sql`,包含每条记录的页号、偏移、是否已删除、可信度、原始数据十六进制

### 2.5 MySQL恢复能力详解

#### 已删除记录恢复(`–recover-deleted`)

InnoDB 使用 COMPACT/DYNAMIC 行格式,`DELETE` 操作只将记录标记为已删除(设置 info_bits 的 DELETED 标志位),记录数据仍残留在页面中,直到被新数据覆盖。FGMDU 通过遍历页面记录链(包括已删除记录链)提取这些”幽灵”记录,标记为 `confidence=50`。

**恢复条件**:记录所在页面未被新数据完全覆盖、页面未被 OPTIMIZE TABLE / ALTER TABLE 重建、记录链中的 next 指针仍然可读。

#### 损坏页面启发式恢复(`–recover-corrupt`)

当 InnoDB 页面的 FIL 头(前 38 字节)损坏时,正常解析会失败。FGMDU 通过搜索页面中的 `infimum` 和 `supremum` 字节标记来定位系统记录,从而重建页面结构:
1. 在页面数据区域搜索 `infimum` 字符串(7 字节 + NULL)
2. 在页面数据区域搜索 `supremum` 字符串(8 字节)
3. 根据找到的位置推断记录链起始位置
4. 尝试从残留的页面头字节中读取 heap_top、index_id 等信息
5. 如果 heap_top 不可读,使用页面大小的 1/4 作为保守估计
6. 遍历记录链提取所有可恢复的记录

**限制**:无法恢复 FIL 头和页面头同时损坏的页面;恢复的记录可能缺少部分元数据(如 index_id)。

#### BLOB 外部页面恢复(`–blob-recovery`)

InnoDB 表包含 BLOB/TEXT 大字段时,数据可能存储在外部页面(BLOB page)中。记录中只保留 20 字节的引用指针:前 4 字节为实际数据长度,后 16 字节为指向外部页面的指针(space_id + page_no + offset)。FGMDU 通过:
1. 扫描记录数据,检测是否包含 BLOB 引用
2. 读取引用指向的外部 BLOB 页面
3. 验证页面类型为 BLOB/ZBLOB
4. 提取 BLOB 页面中的数据(跳过 8 字节 BLOB 头)
5. 如果数据跨越多个 BLOB 页面,沿 BLOB 链追踪
6. 链追踪安全措施:最大 1000 页、循环检测

#### 无字典暴力扫描(`–raw-scan`)

不依赖任何数据字典,直接扫描文件中的所有 INDEX 类型页面,提取记录的原始字节,以十六进制编码输出。适用于表结构信息完全丢失的场景,需要人工或外部脚本解析。

#### 孤儿页面恢复

当表被 DROP 或 TRUNCATE 后,其数据页面可能仍然残留在文件中(尤其是 file-per-table 模式下的 `.ibd` 文件被删除前的残留)。FGMDU 的恢复扫描会尝试提取所有 INDEX 页面中的记录,无论其所属表是否仍然存在。

#### 恢复可信度评级

| 可信度 | 含义 | 场景 |
|——–|——|——|
| 100 | 正常记录 | 页面完整,记录未删除 |
| 50 | 已删除记录 | 记录被 DELETE 标记但数据仍在页面中 |
| 30 | 坏块部分恢复 | 页面有 I/O 坏块,部分数据被抢救恢复 |

### 2.6 稳定性与安全加固

**页面解析安全检查**:
– 页面大小验证:检查是否为 2 的幂且在 4K-64K 范围内
– 页面头完整性:验证 index page header 是否有足够字节读取
– heap_top 边界检查:超出页面范围时自动钳制到合理范围
– n_recs 合理性检查:记录数异常(>50000)时忽略该值

**记录遍历防护**:
– 循环检测:next 指针指回当前或之前的记录位置时立即停止
– 下界检查:记录偏移不能小于 PAGE_NEW_INFIMUM(94)
– 上界检查:记录偏移不能超过 heap_top + 100
– 循环计数器:最多遍历 65536 条记录后强制退出

**BLOB 链追踪防护**:
– 最大链长度:单条 BLOB 数据最多追踪 1000 个外部页面
– 循环检测:BLOB 链中出现页面号重复时立即停止
– 数据长度验证:BLOB 页面声明的数据长度不能超过页面大小

**文件 I/O 防护与坏块处理**:
– NULL 指针检查:所有 I/O 函数入口检查文件描述符和缓冲区指针
– 整数溢出检测:page_no * page_size 的乘积溢出检测
– 短读处理:partial read 时发出警告并返回错误
– 大文件支持:通过 LFS(Large File Support)支持 >2GB 文件
– 坏块自动跳过:I/O 错误时自动降级为分块读取,抢救可读数据
– 多级降级粒度:4096 → 512 → 1 字节,逐级缩小读取粒度
– 坏块统计报告:恢复完成后报告坏块页面数、部分恢复页面数和坏字节数

**内存安全**:
– OOM 处理:所有内存分配通过 xmalloc/xrealloc,OOM 时优雅退出
– 缓冲区初始化:页面读取前 memset 清零,防止残留数据干扰
– 资源释放:所有打开的文件描述符和分配的内存确保释放

### 2.7 跨平台能力

FGMDU 通过 `src/common/portable.h` 和 `portable.c` 实现跨平台抽象:

| 功能 | Linux 实现 | Windows 实现 |
|——|———–|————-|
| 文件打开 | `open()` + `O_RDONLY` | `_sopen_s()` + `_O_BINARY` |
| 文件读取 | `pread()` | `_lseeki64()` + `_read()` 模拟 |
| 文件状态 | `stat()` / `fstat()` | `_stati64()` / `_fstati64()` |
| 目录遍历 | `opendir()` / `readdir()` | `_findfirst64()` / `_findnext64()` |
| 目录创建 | `mkdir(path, 0755)` | `_mkdir(path)` |
| 时间转换 | `localtime_r()` | `localtime_s()` |
| 字符串比较 | `strcasecmp()` | `_stricmp()` |
| 路径分隔符 | `/` | `\` 和 `/` 均支持 |

字节序检测不依赖 `<endian.h>`,有内置回退实现,确保在 RHEL 5 等旧系统上也能编译。

## 3. 支持环境

### 3.1 支持的数据库版本

| 数据库 | 支持版本 |
|——–|———|
| MySQL | 5.6 / 5.7 / 8.0 / 8.4 / 9.7 |
| MariaDB | 全系列 |
| Percona Server | 全系列 |

### 3.2 支持的操作系统

| 平台 | 支持版本 |
|——|———|
| Linux | RHEL 5 / 6 / 7 / 8 / 9 / 10;CentOS / Oracle Linux / Rocky Linux 等兼容发行版;32 位与 64 位 |
| Windows 桌面 | 7 / 8 / 8.1 / 10 / 11 |
| Windows Server | 2008 R2 / 2012 / 2016 / 2019 / 2022 |

### 3.3 编译环境要求

**Linux 编译环境**:
– GCC 4.1+ 或 Clang 3.4+
– C99 支持
– glibc 2.5+(RHEL 5+)
– Make 或 CMake

**Windows 编译环境**:
– Visual Studio 2010+(VC10+)或 Build Tools 2015+
– 或 MinGW-w64(用于从 Linux 交叉编译)
– CMake 2.8+(可选)

### 3.4 运行环境要求

– Linux x86_64 或 x86(32 位)
– Windows x86_64(64 位)
– 足够的磁盘空间存放输出文件(建议为源数据的 2-3 倍)
– 对 MySQL 数据目录的读取权限
– 无需安装 MySQL 客户端库或任何第三方运行时

**RHEL 5/6/7 兼容性说明**:
– 使用 `-D_POSIX_C_SOURCE=200112L` 确保兼容旧版 glibc
– `O_CLOEXEC` 通过条件编译处理(RHEL 5 内核 2.6.18 不支持)
– `posix_fadvise` 自动检测(RHEL 5 glibc 2.5 已有)
– `__builtin_bswap*` 自动检测(GCC 4.1+ 支持)
– 字节序检测不依赖 `<endian.h>`,有内置回退实现

## 4. FGMDU程序使用

### 4.1 编译与安装

FGMDU 支持在 Linux 和 Windows 两个平台上编译,使用同一份 C 源代码,通过 `portable.h` 抽象层处理平台差异。

#### 方式一:Linux Makefile 编译(推荐)

“`bash
# 进入项目目录
cd /path/to/FGMDU

# 直接编译
make

# 编译结果生成可执行文件 fgmdu
./fgmdu version
“`

#### 方式二:Linux CMake 编译

“`bash
mkdir build && cd build
cmake ..
make
“`

#### 方式三:Windows MSVC 编译(NMAKE)

“`cmd
:: 1. 打开 “x64 Native Tools Command Prompt for VS”
:: 2. 进入项目目录
cd C:\path\to\FGMDU

:: 3. 使用 NMAKE 编译
nmake -f Makefile.win

:: 4. 验证
fgmdu.exe version
“`

也可以直接双击 `build_win.bat` 自动检测 Visual Studio 并编译。

#### 方式四:Windows CMake 编译

“`cmd
:: 使用 CMake 生成 Visual Studio 工程
mkdir build
cd build
cmake .. -G “Visual Studio 17 2022″ -A x64

:: 编译
cmake –build . –config Release
“`

#### 方式五:从 Linux 交叉编译 Windows 版本

“`bash
# 安装 MinGW-w64 工具链
# RHEL/Fedora: sudo dnf install mingw64-gcc
# Debian/Ubuntu: sudo apt install gcc-mingw-w64-x86-64

# 交叉编译
./build.sh –mingw
# 或直接: make CROSS=x86_64-w64-mingw32

# 生成的 fgmdu.exe 可直接复制到 Windows 机器运行
“`

#### 编译选项

Makefile 支持以下环境变量:

“`bash
# 指定编译器
CC=gcc make

# 启用调试符号
CFLAGS=”-g -O0″ make

# 交叉编译(如 32 位 Linux)
CFLAGS=”-m32″ LDFLAGS=”-m32″ make

# 交叉编译 Windows 版本
make CROSS=x86_64-w64-mingw32
“`

#### 安装到系统路径

**Linux:**
“`bash
sudo cp fgmdu /usr/local/bin/
fgmdu version
“`

**Windows:**
“`cmd
copy fgmdu.exe C:\Windows\System32\
fgmdu.exe version
“`

### 4.2 命令一览

“`
Usage: fgmdu <command> [options]

Commands:
unload   Extract table data from .ibd/.MYD files into DMP/SQL.
scan     Scan an .ibd file and report page/index structure.
desc     Describe a table from DDL/SDI.
recover  Powerful raw recovery: scan .ibd for deleted/corrupt data.
version  Print version.
help     Show this help.
“`

### 4.3 通用选项

以下选项适用于所有命令:

| 选项 | 说明 | 示例 |
|——|——|——|
| `-D, –data <path>` | 数据文件或目录 | `–data /var/lib/mysql` |
| `-o, –outdir <dir>` | 输出目录(默认 `./fgmdu_out`) | `–outdir /tmp/recovery` |
| `-f, –format <dmp\|sql\|both>` | 输出格式(默认 dmp) | `–format sql` |
| `-e, –engine <innodb\|myisam>` | 强制指定存储引擎 | `–engine innodb` |
| `-t, –table <name>` | 仅处理指定表 | `–table users` |
| `–ddl <file>` | DDL 文件(CREATE TABLE 语句) | `–ddl schema.sql` |
| `–ddl-dir <dir>` | DDL 文件目录(`<table>.sql`) | `–ddl-dir /backup/ddl` |
| `–sdi-dir <dir>` | MySQL 8.0+ SDI 目录 | `–sdi-dir /backup/sdi` |
| `–ibdata <path>` | 系统表空间 ibdata1 | `–ibdata /var/lib/mysql/ibdata1` |
| `–page-size <n>` | 覆盖页面大小(默认自动检测) | `–page-size 8192` |
| `–dict <auto\|innodb-sys\|sdi\|frm\|raw>` | 数据字典来源 | `–dict sdi` |
| `–rows-per-txn <n>` | SQL 批量提交行数(默认 1000) | `–rows-per-txn 5000` |
| `–charset <name>` | 字符集(默认 utf8mb4) | `–charset latin1` |
| `–include-deleted` | 包含已删除记录 | `–include-deleted` |
| `–single-file` | 所有表输出到单个文件 | `–single-file` |
| `–struct-outdir` | 按数据库名建子目录 | `–struct-outdir` |
| `-v, –verbose` | 调试日志 | `–verbose` |
| `-q, –quiet` | 静默模式 | `–quiet` |

### 4.4 unload 命令 – 数据抽取

从 InnoDB `.ibd` 文件或 MyISAM `.MYD` 文件中正常抽取数据。需要数据字典(DDL/SDI/.frm)来解析列类型。

“`bash
# 从单个 .ibd 文件抽取,使用同目录的 .frm 作为字典
fgmdu unload –data /var/lib/mysql/mydb/users.ibd \
–ddl /var/lib/mysql/mydb/users.frm \
–format sql \
–outdir /tmp/recovery

# 从整个数据目录抽取所有表
fgmdu unload –data /var/lib/mysql \
–ddl-dir /backup/ddl \
–format both \
–outdir /tmp/recovery

# 指定 SDI 作为字典(MySQL 8.0+)
fgmdu unload –data /var/lib/mysql/mydb/orders.ibd \
–sdi-dir /var/lib/mysql/mydb \
–format dmp \
–outdir /tmp/recovery

# 递归扫描整个 MySQL 数据目录,按数据库自动建子目录
fgmdu unload –data /var/lib/mysql \
–ddl-dir /backup/ddl \
–format sql \
–struct-outdir \
–outdir /tmp/full_recovery
“`

**输出文件命名**:
– 默认:`<outdir>/<database>.<table>.dmp` 或 `.sql`
– `–struct-outdir`:`<outdir>/<database>/<table>.dmp` 或 `.sql`
– `–single-file` 可合并为单个文件(不推荐,文件过大)

**进度反馈**:抽取过程中实时输出每张表的表名、页大小、文件大小、抽取行数:
“`
[INFO]  innodb ‘/var/lib/mysql/mydb/users.ibd’: page_size=16384, size=10485760 bytes
[OK] users: 50000 rows -> /tmp/recovery
“`

### 4.5 scan 命令 – 页面结构扫描

扫描 InnoDB 文件,报告页面类型分布和索引结构,用于诊断文件损坏程度。

“`bash
# 扫描 .ibd 文件
fgmdu scan –data /var/lib/mysql/mydb/users.ibd

# 扫描 ibdata1
fgmdu scan –data /var/lib/mysql/ibdata1

# 指定页面大小
fgmdu scan –data /var/lib/mysql/mydb/large.ibd –page-size 32768
“`

**输出示例**:
“`
page 0: FSP_HDR
page 1: INDEX leaf index_id=1234 n_recs=500
page 2: INDEX leaf index_id=1234 n_recs=480
page 3: INDEX lvl=1 index_id=1234
page 4: BLOB
page 5: INDEX leaf index_id=1234 n_recs=510

Summary:
FSP_HDR              8: 1 pages
INDEX            17855: 50 pages
BLOB                 10: 5 pages
ALLOCATED             0: 10 pages
clustered index_id=1234 (48 leaf pages)
“`

**诊断用途**:
– INDEX 页面数量为 0:文件可能已损坏或被覆盖
– 大量 ALLOCATED 页面:表可能刚创建,数据量少
– BLOB 页面较多:表中包含大字段数据
– INDEX 页面 n_recs=0:可能是空页或叶子节点指针损坏

### 4.6 desc 命令 – 表结构描述

从 DDL 文件或 SDI 文件加载并显示表结构信息。

“`bash
# 从 DDL 文件描述表结构
fgmdu desc –ddl /backup/ddl/users.sql

# 从 SDI 目录描述表结构
fgmdu desc –sdi-dir /var/lib/mysql/mydb –table users

# 从 .frm 文件描述表结构
fgmdu desc –ddl /var/lib/mysql/mydb/users.frm
“`

**输出示例**:
“`
column                  type               len     pack flags
id                      INT                   4        4 NOT NULL
name                    VARCHAR              50        2 NULL
email                   VARCHAR             255        2 NULL
created_at              DATETIME             5        5 NULL
balance                 DECIMAL             10        5 NULL UNSIGNED
status                  ENUM                  1        1 NULL
“`

### 4.7 recover 命令 – 灾难恢复

`recover` 是 FGMDU 最强大的功能,用于在以下场景恢复数据:表被 DROP 后的页面残留数据、DELETE 删除的记录(仍在页面空闲链中)、页面 FIL 头损坏的数据、无数据字典的原始数据扫描、BLOB 外部页面数据恢复。

**恢复专用选项**:

| 选项 | 说明 |
|——|——|
| `–recover-deleted` | 提取已删除记录 |
| `–recover-corrupt` | 对损坏页面执行启发式恢复 |
| `–recover-meta` | 输出恢复元数据(页号、可信度) |
| `–raw-scan` | 无字典模式:暴力扫描所有 INDEX 记录 |
| `–target-table <name>` | 按表名子串过滤 |
| `–filter-database <db>` | 按数据库名过滤 |
| `–filter-user <user>` | 按用户名过滤(路径匹配) |
| `–filter-object <name>` | 按对象名过滤 |
| `–blob-recovery` | 恢复 BLOB 外部页面数据 |
| `–data-check <off\|basic\|strict>` | 数据完整性检查级别(默认 basic) |

**模式一:基本恢复(推荐首选)**
“`bash
fgmdu recover –data /var/lib/mysql/mydb/users.ibd \
–recover-deleted –recover-corrupt –recover-meta
“`

默认启用已删除记录恢复和损坏页面恢复。输出包含每条记录的来源页号、偏移量、是否已删除和恢复可信度。

**模式二:原始暴力扫描(无字典)**
“`bash
fgmdu recover –data /var/lib/mysql/mydb/users.ibd \
–raw-scan –recover-deleted –recover-corrupt
“`

不需要任何数据字典,直接扫描所有 INDEX 页面,提取原始记录字节并以十六进制输出。适用于表结构信息完全丢失的场景。

**模式三:完整恢复(所有选项启用)**
“`bash
fgmdu recover –data /var/lib/mysql/mydb/users.ibd \
–recover-deleted \
–recover-corrupt \
–recover-meta \
–blob-recovery \
–raw-scan \
–outdir /tmp/full_recovery
“`

**模式四:从整个数据目录恢复**
“`bash
fgmdu recover –data /var/lib/mysql \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery
“`

递归扫描所有子目录,自动发现 `.ibd` 和 `ibdata1` 文件。

**恢复输出格式**(输出为 `recovered_data.sql`):
“`sql
— FGMDU Recovery Output
— Source: /var/lib/mysql/mydb/users.ibd
— Options: deleted=yes corrupt_pages=yes raw_scan=no

— RECORD: page=1 offset=112 deleted=0 confidence=100 len=14
INSERT INTO _recovered (page_no, rec_off, deleted, data_hex) VALUES (1, 112, 0, ‘5245434F52445F315F4F4B000000’);

— RECORD: page=2 offset=112 deleted=1 confidence=50 len=15
INSERT INTO _recovered (page_no, rec_off, deleted, data_hex) VALUES (2, 112, 1, ‘44454C455445445F5245435F310000′);
“`

### 4.8 MySQL坏块跳过与部分恢复

当存储介质出现物理坏块(bad block)时,读取该区域会返回 I/O 错误(EIO)。FGMDU 的坏块跳过机制可以最大程度地抢救坏块周围的可读数据,**该机制自动启用,无需额外选项**。

**工作原理(三级递进策略)**:
1. **正常整页读取**:首先尝试一次性读取整个页面(16KB)。如果成功,正常处理。
2. **分块降级读取**:如果整页读取失败,按 4096 字节 → 512 字节 → 1 字节的粒度逐块重试。可读部分填入缓冲区,不可读部分用零填充。
3. **启发式恢复**:对部分读取的页面,尝试搜索 `infimum`/`supremum` 标记来重建页面结构,提取残存的记录。

**坏块统计报告**:
“`
[WARN]  page 42: bad block detected (4096 bytes unreadable), attempting partial recovery (12288 bytes good)
[WARN]  page 87: bad block detected (512 bytes unreadable), attempting partial recovery (15872 bytes good)
[WARN]  2 pages had bad blocks (I/O errors); 2 partially recovered (4608 bad bytes total)
[INFO]  Recovery complete: 523 records from 100 pages
“`

**使用示例**:
“`bash
# 从有坏块的磁盘恢复数据(坏块跳过自动启用)
fgmdu recover –data /dev/sdb1 \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery

# 从有坏块的 .ibd 文件恢复
fgmdu recover –data /var/lib/mysql/mydb/large_table.ibd \
–recover-deleted –recover-corrupt –recover-meta \
–outdir /tmp/recovery
“`

**最佳实践**:对于严重坏块的磁盘,先用 `ddrescue` 创建镜像文件,再在镜像上恢复:
“`bash
yum install ddrescue
ddrescue -r3 /dev/sdb1 /tmp/disk_image.img /tmp/rescue.log
fgmdu recover –data /tmp/disk_image.img –recover-deleted –recover-corrupt
“`

### 4.9 MySQL按用户名/数据库/对象名过滤

FGMDU 支持按 MySQL 数据库实例的路径结构进行过滤恢复。MySQL 数据目录的典型布局:

“`
/var/lib/mysql/           <- MySQL 数据根目录
├── ibdata1               <- 系统表空间
├── mysql/                <- 系统数据库
│   ├── user.ibd
│   └── …
├── mydb/                 <- 用户数据库 “mydb”
│   ├── users.ibd
│   ├── orders.ibd
│   └── products.ibd
├── testdb/               <- 用户数据库 “testdb”
│   └── …
└── /home/user/mysql_data/ <- 某用户的自定义数据目录
“`

**按数据库名过滤**(路径中包含 `/mydb/` 段的文件才会被处理):
“`bash
fgmdu recover –data /var/lib/mysql \
–filter-database mydb \
–recover-deleted –recover-corrupt –recover-meta
“`

**按用户名过滤**(路径中包含 `/user/` 段的文件才会被处理):
“`bash
fgmdu recover –data /home \
–filter-user user \
–recover-deleted –recover-corrupt
“`

**按对象名过滤**(文件名去掉扩展名后精确匹配或包含指定字符串):
“`bash
fgmdu recover –data /var/lib/mysql \
–filter-object users \
–recover-deleted –recover-corrupt –recover-meta
“`

**组合过滤**:
“`bash
# 恢复 mydb 数据库中的 users 表
fgmdu recover –data /var/lib/mysql \
–filter-database mydb \
–filter-object users \
–recover-deleted –recover-corrupt –recover-meta –blob-recovery
“`

过滤选项在 `unload` 命令中同样可用,可与 `–struct-outdir` 任意组合,过滤生效后仍按被命中的表所属数据库名建立子目录。

### 4.10 输出格式说明

#### DMP 格式

FGMDU 自定义的二进制导出格式,每个表一个 `.dmp` 文件。

**文件结构**:
“`
[文件头]
– Magic: “FGMDU” (5 bytes)
– Version: 1 (2 bytes)
– Table name length + name
– Column count (2 bytes)
– Per-column metadata (type, length, name)

[数据行]
– Row length (4 bytes, big-endian)
– Row data (variable length)
– … 重复

[文件尾]
– 0xFFFFFFFF (4 bytes, 表示结束)
“`

#### SQL 格式

标准 SQL INSERT 语句,支持事务批处理。

“`sql
— FGMDU SQL Export
— Table: mydb.users
— Columns: 5

SET autocommit=0;
START TRANSACTION;

INSERT INTO `mydb`.`users` (`id`,`name`,`email`,`created_at`,`balance`) VALUES (1,’alice’,’alice@example.com’,’2024-01-15 10:30:00′,100.50);
INSERT INTO `mydb`.`users` (`id`,`name`,`email`,`created_at`,`balance`) VALUES (2,’bob’,’bob@example.com’,’2024-01-15 11:00:00′,200.00);
— … (每 rows_per_txn 行后)
COMMIT;
START TRANSACTION;
— …

COMMIT;
“`

#### 恢复输出格式

恢复命令输出为 SQL 文件,包含元数据注释和十六进制数据:
“`sql
— RECORD: page=1 offset=112 deleted=0 confidence=100 len=14
INSERT INTO _recovered (page_no, rec_off, deleted, data_hex)
VALUES (1, 112, 0, ‘5245434F52445F315F4F4B000000’);
“`

**字段说明**:
– `page_no`:记录所在的 InnoDB 页号
– `rec_off`:记录在页面内的字节偏移
– `deleted`:0=存活记录,1=已删除记录
– `confidence`:恢复可信度(100=正常提取,50=已删除/损坏恢复,30=坏块页面部分恢复)
– `data_hex`:原始记录数据的十六进制编码

## 5. MySQL各种案例场景与操作过程

### 案例一:MySQL数据库无法启动,正常抽取全部数据

**场景**:MySQL 服务无法启动(如 my.cnf 配置错误),但数据文件完好。

“`bash
# 步骤 1:确认数据文件位置
ls -la /var/lib/mysql/

# 步骤 2:准备 DDL 文件(从备份或 .frm 文件提取)
ls /backup/schema/

# 步骤 3:抽取所有数据库的所有表
fgmdu unload –data /var/lib/mysql \
–ddl-dir /backup/schema \
–format sql \
–outdir /tmp/recovery \
–rows-per-txn 5000

# 步骤 4:验证输出
ls /tmp/recovery/
# 预期:mydb.users.sql  mydb.orders.sql  …

# 步骤 5:在新数据库中导入
mysql -u root -p < /tmp/recovery/mydb.users.sql
“`

### 案例二:MySQL单表 .ibd 文件损坏,抽取正常页面数据

**场景**:某个表的 `.ibd` 文件部分页面损坏,MySQL 报错无法读取该表。

“`bash
# 步骤 1:扫描文件查看损坏程度
fgmdu scan –data /var/lib/mysql/mydb/orders.ibd

# 步骤 2:尝试正常抽取
fgmdu unload –data /var/lib/mysql/mydb/orders.ibd \
–ddl /backup/schema/orders.sql \
–format sql \
–outdir /tmp/recovery

# 步骤 3:如果正常抽取失败,使用恢复模式
fgmdu recover –data /var/lib/mysql/mydb/orders.ibd \
–recover-corrupt –recover-meta \
–outdir /tmp/recovery

# 步骤 4:查看恢复结果
cat /tmp/recovery/recovered_data.sql
“`

### 案例三:MySQL误删数据恢复(DELETE FROM)

**场景**:误执行 `DELETE FROM users WHERE status = ‘inactive’`,需要恢复被删除的记录。

“`bash
# 立即停止 MySQL 服务,防止被删数据被覆盖
systemctl stop mysqld

# 复制 .ibd 文件到安全位置
cp /var/lib/mysql/mydb/users.ibd /tmp/users_backup.ibd

# 使用恢复模式提取已删除记录
fgmdu recover –data /tmp/users_backup.ibd \
–recover-deleted –recover-meta \
–outdir /tmp/recovery

# 查看恢复结果,deleted=1 的记录就是被删除的数据
grep “deleted=1″ /tmp/recovery/recovered_data.sql

# 将十六进制数据解码为可读格式(需要根据表结构手动解析)
“`

### 案例四:MySQL表被 DROP 后的恢复

**场景**:误执行 `DROP TABLE mydb.important_table`,表文件已从文件系统中删除。

“`bash
# 情况 A:如果使用 file-per-table(innodb_file_per_table=ON)
# 且文件刚被删除,可能在文件系统层面恢复

# 使用 extundelete 或类似工具恢复被删除的 .ibd 文件
extundelete /dev/sda1 –restore-file var/lib/mysql/mydb/important_table.ibd

# 然后使用 FGMDU 恢复
fgmdu recover –data /tmp/restored/important_table.ibd \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery

# 情况 B:如果使用共享表空间(innodb_file_per_table=OFF)
# 数据在 ibdata1 中,页面可能仍残留
fgmdu recover –data /var/lib/mysql/ibdata1 \
–target-table important_table \
–recover-deleted –recover-corrupt –recover-meta \
–outdir /tmp/recovery
“`

### 案例五:MySQL从整个数据目录按数据库恢复

**场景**:整个 MySQL 数据目录损坏,需要按数据库分批恢复。

“`bash
# 恢复 mydb 数据库的所有表
fgmdu recover –data /var/lib/mysql \
–filter-database mydb \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery_mydb

# 恢复 testdb 数据库的所有表
fgmdu recover –data /var/lib/mysql \
–filter-database testdb \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery_testdb

# 仅恢复特定对象
fgmdu recover –data /var/lib/mysql \
–filter-database mydb \
–filter-object users \
–recover-deleted –recover-corrupt –recover-meta \
–outdir /tmp/recovery_users
“`

### 案例六:MySQL无数据字典的原始恢复

**场景**:数据库完全损坏,DDL、SDI、`.frm` 文件全部丢失,只有 `.ibd` 文件。

“`bash
# 使用原始暴力扫描模式
fgmdu recover –data /var/lib/mysql/mydb/users.ibd \
–raw-scan –recover-deleted –recover-corrupt \
–recover-meta –blob-recovery \
–outdir /tmp/recovery

# 输出为十六进制格式,需要人工分析
# data_hex 字段包含原始记录数据

# 可以使用 Python 等工具解析十六进制数据:
python3 -c ”
data = bytes.fromhex(‘5245434F52445F315F4F4B000000′)
print(data)
# 输出: b’RECORD_1_OK\\x00\\x00\\x00’

“`

### 案例七:MySQL MyISAM 表数据恢复

**场景**:MyISAM 表的 `.MYD` 数据文件损坏,MySQL 无法读取。

“`bash
# 抽取 MyISAM 表数据
fgmdu unload –data /var/lib/mysql/mydb/logs.MYD \
–ddl /backup/schema/logs.sql \
–engine myisam \
–format sql \
–outdir /tmp/recovery

# 包含已删除记录
fgmdu unload –data /var/lib/mysql/mydb/logs.MYD \
–ddl /backup/schema/logs.sql \
–engine myisam \
–include-deleted \
–format sql \
–outdir /tmp/recovery
“`

### 案例八:MySQL 8.0+ 使用 SDI 恢复

**场景**:MySQL 8.0+ 数据库,使用 SDI(Serialized Dictionary Information)存储表结构。

“`bash
# SDI 文件通常与 .ibd 文件在同一目录
ls /var/lib/mysql/mydb/*.sdi

# 使用 SDI 作为数据字典
fgmdu unload –data /var/lib/mysql/mydb/users.ibd \
–sdi-dir /var/lib/mysql/mydb \
–format sql \
–outdir /tmp/recovery

# 或者从 SDI 描述表结构
fgmdu desc –sdi-dir /var/lib/mysql/mydb –table users
“`

### 案例九:MySQL大表恢复(>2GB .ibd 文件)

**场景**:表数据量很大,`.ibd` 文件超过 2GB。

“`bash
# FGMDU 支持 LFS(大文件支持),可直接处理 >2GB 文件
fgmdu unload –data /var/lib/mysql/mydb/large_table.ibd \
–ddl /backup/schema/large_table.sql \
–format sql \
–rows-per-txn 10000 \
–outdir /tmp/recovery

# 对于超大文件,建议使用 –quiet 减少日志输出
fgmdu recover –data /var/lib/mysql/mydb/huge_table.ibd \
–recover-deleted –recover-corrupt \
–quiet \
–outdir /tmp/recovery
“`

### 案例十:MySQL从 ibdata1 恢复数据

**场景**:使用共享表空间模式(`innodb_file_per_table=OFF`),所有表数据存储在 `ibdata1` 中。

“`bash
# 扫描 ibdata1 查看页面结构
fgmdu scan –data /var/lib/mysql/ibdata1

# 从 ibdata1 恢复特定表
fgmdu recover –data /var/lib/mysql/ibdata1 \
–target-table users \
–recover-deleted –recover-corrupt –recover-meta \
–outdir /tmp/recovery

# 从 ibdata1 恢复全部数据
fgmdu recover –data /var/lib/mysql/ibdata1 \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery
“`

### 案例十一:MySQL按用户名过滤恢复

**场景**:某用户将自己的 MySQL 数据目录放在 `/home/alice/mysql_data/`,需要仅恢复该用户的数据。

“`bash
# 按用户名过滤
fgmdu recover –data /home \
–filter-user alice \
–recover-deleted –recover-corrupt –recover-meta \
–outdir /tmp/recovery_alice

# 组合过滤:alice 用户的 mydb 数据库
fgmdu recover –data /home \
–filter-user alice \
–filter-database mydb \
–recover-deleted –recover-corrupt \
–outdir /tmp/recovery_alice_mydb
“`

### 案例十二:MySQL完整灾难恢复流程

**场景**:服务器崩溃,MySQL 完全无法启动,需要尽可能恢复所有数据。

“`bash
# 步骤 1:停止 MySQL 服务,防止进一步损坏
systemctl stop mysqld

# 步骤 2:创建数据副本(永远在副本上操作)
cp -a /var/lib/mysql /tmp/mysql_backup

# 步骤 3:扫描评估损坏程度
fgmdu scan –data /tmp/mysql_backup/ibdata1
fgmdu scan –data /tmp/mysql_backup/mydb/users.ibd

# 步骤 4:尝试正常抽取(需要 DDL)
fgmdu unload –data /tmp/mysql_backup \
–ddl-dir /backup/schema \
–format both \
–outdir /tmp/recovery_normal

# 步骤 5:对无法正常抽取的表执行恢复
fgmdu recover –data /tmp/mysql_backup \
–filter-database mydb \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery_mydb

fgmdu recover –data /tmp/mysql_backup \
–filter-database testdb \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery_testdb

# 步骤 6:对完全无法解析的文件执行原始扫描
fgmdu recover –data /tmp/mysql_backup/corrupt_table.ibd \
–raw-scan –recover-corrupt \
–outdir /tmp/recovery_raw

# 步骤 7:验证恢复数据
ls -la /tmp/recovery_*/
wc -l /tmp/recovery_*/*.sql

# 步骤 8:在新服务器上导入恢复数据
mysql -u root -p -e “CREATE DATABASE mydb”
mysql -u root -p mydb < /tmp/recovery_normal/mydb.users.sql
“`

### 案例十三:MySQL Windows 平台数据恢复

**场景**:MySQL 部署在 Windows 服务器上,数据库损坏无法启动,需要从 `.ibd` 文件恢复数据。

**Windows 上的 MySQL 数据目录典型路径**:
– `C:\ProgramData\MySQL\MySQL Server 8.0\Data\`(默认安装)
– `D:\MySQL\Data\`(自定义安装)

“`cmd
:: 步骤 1:停止 MySQL 服务
net stop MySQL80

:: 步骤 2:复制 .ibd 文件到安全位置
copy “C:\ProgramData\MySQL\MySQL Server 8.0\Data\fgedudb\users.ibd” “D:\recovery\users.ibd”

:: 步骤 3:准备 DDL 文件(从备份获取)
:: 假设 DDL 存放在 D:\backup\schema\users.sql

:: 步骤 4:抽取数据
fgmdu.exe unload –data “D:\recovery\users.ibd” ^
–ddl “D:\backup\schema\users.sql” ^
–table users ^
–format sql ^
–outdir “D:\recovery\output”

:: 步骤 5:恢复已删除记录
fgmdu.exe recover –data “D:\recovery\users.ibd” ^
–recover-deleted –recover-meta ^
–outdir “D:\recovery\recover_output”

:: 步骤 6:从整个数据目录恢复
fgmdu.exe recover –data “C:\ProgramData\MySQL\MySQL Server 8.0\Data” ^
–filter-database mydb ^
–recover-deleted –recover-corrupt –recover-meta ^
–outdir “D:\recovery\mydb_output”

:: 步骤 7:查看恢复结果
type “D:\recovery\output\users.sql”

:: 步骤 8:导入到新数据库
mysql -u root -p -e “CREATE DATABASE mydb”
mysql -u root -p mydb < “D:\recovery\output\users.sql”
“`

**Windows 路径说明**:
– FGMDU 同时支持 `/` 和 `\` 路径分隔符
– 路径包含空格时需用双引号括起
– `–filter-database` / `–filter-user` 过滤器在 Windows 上同样基于路径段匹配
– Windows 上的 MySQL 数据目录结构:`<DataDir>\<database>\<table>.ibd`

### 案例十四:从 Linux 恢复 Windows MySQL 数据

**场景**:Windows MySQL 服务器崩溃,将数据文件复制到 Linux 恢复服务器上使用 FGMDU 恢复。

“`bash
# 步骤 1:将 Windows 数据文件复制到 Linux 恢复服务器
scp admin@win-server:”/C:/ProgramData/MySQL/MySQL Server 8.0/Data/mydb/users.ibd” /tmp/recovery/

# 步骤 2:同时复制 DDL 备份
scp admin@win-server:”/D:/backup/schema/users.sql” /tmp/recovery/ddl/

# 步骤 3:在 Linux 上执行恢复
fgmdu unload –data /tmp/recovery/users.ibd \
–ddl /tmp/recovery/ddl/users.sql \
–format sql \
–outdir /tmp/recovery/output

# 步骤 4:将恢复结果传回 Windows 或直接导入 Linux MySQL
mysql -u root -p -e “CREATE DATABASE mydb”
mysql -u root -p mydb < /tmp/recovery/output/users.sql
“`

### 案例十五:MySQL数据库无法启动——全库抽取并按数据库名生成子目录

**场景**:服务器宕机后 MySQL 无法启动(例如 ib_logfile 损坏、系统表空间崩溃、权限问题),但 `.ibd` / `.MYD` 数据文件仍然完整可读。此时无法通过 SQL 接口导出,需由 FGMDU 直接扫描整个数据目录,按数据库/用户名自动建子目录,把所有能解析的表都抽取出来。

**适用条件**:
– 数据库进程无法启动
– 有 DDL 备份(`–ddl-dir`)、或 MySQL 8.0 的 `.sdi` 文件(`–sdi-dir`)、或数据目录下仍保留 `.frm` 文件
– 若以上字典都丢失,可使用 `–dict raw`(InnoDB 5.7 之前 .frm 丢失,依赖外部 DDL)或 `recover –raw-scan` 按无字典恢复

**输出结构**:

默认(向后兼容):
“`
<outdir>/
├── db1.table1.sql
├── db1.table2.sql
└── db2.table1.sql
“`

使用 `–struct-outdir` 后:
“`
<outdir>/
├── db1/
│   ├── table1.sql
│   ├── table1.dmp
│   └── table2.sql
└── db2/
└── table1.sql
“`

**操作**:
“`bash
# 步骤 1:保护原始数据(在只读副本上工作!)
cp -a /var/lib/mysql /tmp/mysql_backup
ls -la /tmp/mysql_backup/
# 可以看到:mydb/  orderdb/  testdb/  mysql/  ibdata1  …

# 步骤 2:全库 unload,按数据库名自动生成子目录
fgmdu unload –data /tmp/mysql_backup \
–ddl-dir /backup/schema \
–format both \
–struct-outdir \
–outdir /tmp/full_recovery

# 输出目录结构示例:
# /tmp/full_recovery/mydb/users.sql
# /tmp/full_recovery/mydb/users.dmp
# /tmp/full_recovery/mydb/orders.sql
# /tmp/full_recovery/orderdb/item.sql
# /tmp/full_recovery/testdb/t1.sql

# 步骤 3:若有 MySQL 8.0,优先使用 .sdi 文件获取表结构
fgmdu unload –data /tmp/mysql_backup \
–sdi-dir /tmp/mysql_backup \
–format sql \
–struct-outdir \
–outdir /tmp/full_recovery_sdi

# 步骤 4:只抽取特定数据库
fgmdu unload –data /tmp/mysql_backup \
–ddl-dir /backup/schema \
–filter-database mydb \
–struct-outdir \
–format sql \
–outdir /tmp/recovery_mydb_only

# 步骤 5:导入到新实例(按数据库逐个导入)
for db in /tmp/full_recovery/*/; do
dbname=$(basename “$db”)
mysql -u root -p -e “CREATE DATABASE IF NOT EXISTS \`$dbname\` DEFAULT CHARSET utf8mb4;”
for sql in “$db”*.sql; do
[ -f “$sql” ] && mysql -u root -p “$dbname” < “$sql”
done
done
“`

**Windows 版本**:
“`cmd
:: 步骤 1:停止 MySQL 服务并备份
net stop MySQL80
robocopy “C:\ProgramData\MySQL\MySQL Server 8.0\Data” “D:\recovery\mysql_backup” /E /COPY:DAT

:: 步骤 2:全库抽取并按数据库名分目录
fgmdu.exe unload –data “D:\recovery\mysql_backup” ^
–ddl-dir “D:\backup\schema” ^
–format both ^
–struct-outdir ^
–outdir “D:\recovery\output”

:: 步骤 3:查看结果
dir /s /b “D:\recovery\output”
::  D:\recovery\output\fgedudb\users.sql
::  D:\recovery\output\fgedudb\users.dmp
::  D:\recovery\output\orderdb\item.sql
“`

### 案例十六:MySQL按用户维度全库抽取(共享主机 / 多用户部署)

**场景**:共享主机上不同用户把各自 MySQL 数据放在 `/home/<user>/mysql_data/`、`/home/<user>/data/mysql/` 等路径。数据库实例无法启动后,需要按用户名恢复,把每个用户的所有数据库/表导出到独立目录,交还给用户。

“`bash
# 目录结构:
# /home/
#   ├── alice/mysql_data/mydb/users.ibd
#   ├── alice/mysql_data/orderdb/orders.ibd
#   ├── bob/mysql_data/blogdb/posts.ibd
#   └── bob/mysql_data/blogdb/comments.ibd

# 步骤 1:仅恢复 alice 用户,按数据库分子目录
fgmdu unload –data /home \
–ddl-dir /backup/schema \
–filter-user alice \
–struct-outdir \
–format sql \
–outdir /tmp/alice_recovery

# 输出(每个数据库一个子目录):
# /tmp/alice_recovery/mydb/users.sql
# /tmp/alice_recovery/orderdb/orders.sql

# 步骤 2:同时按用户 + 数据库过滤
fgmdu unload –data /home \
–ddl-dir /backup/schema \
–filter-user bob \
–filter-database blogdb \
–struct-outdir \
–format sql \
–outdir /tmp/bob_blogdb_recovery

# 输出:/tmp/bob_blogdb_recovery/blogdb/posts.sql
#      /tmp/bob_blogdb_recovery/blogdb/comments.sql

# 步骤 3:一次性为所有用户导出(结合 shell 循环 + –filter-user)
for user in alice bob charlie; do
out=”/tmp/all_users/${user}”
mkdir -p “$out”
fgmdu unload –data /home \
–ddl-dir /backup/schema \
–filter-user “$user” \
–struct-outdir \
–format both \
–outdir “$out”
done

# 最后得到:
# /tmp/all_users/alice/mydb/users.sql
# /tmp/all_users/alice/orderdb/orders.sql
# /tmp/all_users/bob/blogdb/posts.sql
# …
“`

> **提示**:`–struct-outdir` 与 `–filter-database` / `–filter-user` / `–filter-object` 可以任意组合,过滤生效后,仍按被命中的表所属数据库名建立子目录。

### 案例十七:MySQL坏块磁盘的渐进式恢复

**场景**:存储磁盘出现物理坏块,`dd` 直接读取报 I/O 错误,MySQL 完全无法启动,需要尽可能抢救数据。

“`bash
# 步骤 1:先用 ddrescue 创建镜像(跳过坏块,多次重试边缘数据)
yum install ddrescue
ddrescue -r3 /dev/sdb1 /tmp/disk_image.img /tmp/rescue.log

# 步骤 2:在镜像上扫描评估
fgmdu scan –data /tmp/disk_image.img

# 步骤 3:直接对块设备或镜像执行恢复(坏块跳过自动启用)
fgmdu recover –data /dev/sdb1 \
–recover-deleted –recover-corrupt –recover-meta \
–blob-recovery \
–outdir /tmp/recovery

# 步骤 4:检查坏块统计报告
# 关注 confidence=30 的记录,这些是从坏块页面抢救的,可能不完整

# 步骤 5:对 confidence=30 的记录人工核验
grep “confidence=30” /tmp/recovery/recovered_data.sql
“`

## 6. 常用问题与排查

### 6.1 常见问题

#### Q1:提示 “page too small or NULL”

**A:** 文件不是有效的 InnoDB 页面文件,或文件为空。请检查文件路径和文件完整性。可用 `file` 命令确认文件类型,用 `ls -la` 确认文件大小非零。

#### Q2:提示 “no schema source found for table”

**A:** FGMDU 找不到数据字典文件。请通过以下方式之一提供:
– `–ddl <file>` 指定 DDL 文件
– `–ddl-dir <dir>` 指定 DDL 文件目录
– `–sdi-dir <dir>` 指定 SDI 目录(MySQL 8.0+)
– 使用 `–raw-scan` 跳过字典依赖

#### Q3:恢复出 0 条记录

**A:** 可能原因及解决方案:
1. 文件不是 InnoDB 格式 – 使用 `scan` 命令检查
2. 页面全部损坏 – 尝试 `–raw-scan –recover-corrupt`
3. 数据在 ibdata1 而非 .ibd 中 – 使用 `–data ibdata1路径`
4. 页面大小不匹配 – 使用 `–page-size` 指定正确大小

#### Q4:恢复的记录数据是十六进制,如何解析?

**A:** 十六进制数据是原始 InnoDB 记录格式。需要根据表结构手动解析:
– 使用 `desc` 命令获取列定义
– 根据列类型(INT、VARCHAR、DATETIME 等)从十六进制中提取字段值
– 或者编写脚本批量解码

#### Q5:编译时提示缺少 endian.h

**A:** FGMDU 自动检测系统是否支持 `endian.h`。如果不可用,会自动使用内置的字节序处理函数。如果编译仍然失败,请确保使用 C99 标准编译(GCC 4.1+)。

#### Q6:处理大文件时内存不足

**A:** FGMDU 一次只读取一个页面(16KB),内存占用很低。如果仍然 OOM:
– 使用 `–quiet` 减少日志缓冲
– 检查系统可用内存
– 确认文件没有损坏导致异常分配

#### Q7:磁盘有坏块,恢复时遇到 I/O 错误怎么办?

**A:** FGMDU 自动处理坏块,无需额外选项。遇到 I/O 错误时:
1. 自动降级为分块读取(4096 → 512 → 1 字节)
2. 可读部分填入缓冲区,不可读部分用零填充
3. 对部分读取的页面尝试启发式恢复
4. 坏块页面恢复的记录标记为 `confidence=30`
5. 恢复完成后报告坏块统计信息

如果坏块非常严重,建议先用 `ddrescue` 创建镜像:
“`bash
ddrescue -r3 /dev/sdb1 /tmp/disk_image.img /tmp/rescue.log
fgmdu recover –data /tmp/disk_image.img –recover-deleted –recover-corrupt
“`

#### Q8:如何恢复 TRUNCATE TABLE 后的数据?

**A:** TRUNCATE 会重建表文件,原数据页面可能被覆盖。如果使用 file-per-table 模式:
1. 原 `.ibd` 文件已被新文件替换
2. 需要在文件系统层面恢复被删除的旧 `.ibd` 文件
3. 使用 `extundelete` 或 `testdisk` 等工具恢复
4. 然后使用 FGMDU 的 `recover` 命令提取数据

#### Q9:Windows 路径包含空格无法识别?

**A:** 在 Windows 命令行中,路径包含空格时必须用双引号括起,例如:
“`cmd
fgmdu.exe unload –data “D:\My Data\users.ibd” –outdir “D:\Recovery Output”
“`
FGMDU 同时支持 `/` 和 `\` 路径分隔符,可混合使用。

#### Q10:如何在 Windows 上从命令行停止 MySQL 服务?

**A:** 使用 `net stop` 命令:
“`cmd
net stop MySQL80
:: 或
net stop MySQL57
:: 服务名取决于安装版本
“`

#### Q11:scan 命令显示 INDEX 页面为 0 但文件大小正常?

**A:** 可能原因:
1. 文件是 MyISAM 的 `.MYD` 而非 InnoDB 的 `.ibd`,scan 只支持 InnoDB
2. 页面大小不匹配,尝试 `–page-size 4096` 或 `–page-size 32768`
3. 文件头部已损坏,FIL 头无法识别

#### Q12:recover 命令默认就启用了哪些选项?

**A:** 如果没有显式指定 `–recover-deleted` 或 `–recover-corrupt`,recover 命令会自动默认启用这两个选项(安全优先)。如需禁用,请显式指定其他选项组合。`–raw-scan` 在没有 DDL 时也会自动启用。

### 6.2 日志级别

– **INFO**:正常进度信息
– **WARN**:警告,可能影响部分输出
– **ERROR**:错误,可能导致恢复失败
– **DEBUG**:调试信息(使用 `-v` 启用)

### 6.3 最佳实践

1. **永远在副本上操作**:不要直接操作原始数据文件,先 `cp -a` 创建副本
2. **先扫描后恢复**:使用 `scan` 命令评估损坏程度,制定恢复策略
3. **先正常后恢复**:先尝试 `unload`,失败后再用 `recover`,从易到难递进
4. **保留恢复元数据**:使用 `–recover-meta` 记录数据来源(页号、可信度)
5. **分批恢复**:使用过滤选项分数据库/表恢复,避免一次性处理过多数据
6. **验证数据**:恢复后务必检查数据的完整性和正确性,特别是 `confidence=30` 的记录
7. **坏块先镜像**:对于物理坏块磁盘,先用 `ddrescue` 创建镜像再操作
8. **大文件用 LFS**:FGMDU 默认支持 LFS,可直接处理 >2GB 文件,无需特殊处理


## 关于作者

| 联系方式 | 信息 |
|———|——|
| **作者** 风哥
| **WX**  itpux-com
| **QQ**  113257174
| **官方网站** : http://www.fgedu.net.cn , http://www.itpux.com
| **数据库教程** : https://edu.51cto.com/lecturer/8020378.html

*FGMDU 1.0 – fgedu mysql dul*
*支持 MySQL 5.6/5.7/8.0/8.4/9.7, MariaDB, Percona*
*目标操作系统:Linux RHEL 5/6/7/8/9/10,Windows 7/8/8.1/10/11,Server 2008 R2/2012/2016/2019/2022*

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息