SQLServer数据库恢复工具FGSDU(FGEDU SQLServer DUL)
SQLServer数据库恢复工具FGPDU(FGEDU SQLServer DUL)
目录
1. 程序介绍
1.1 程序概述
FGSDU(全称 fgedu SQLServer dul,即 fgedu SQLServer Data Unload Loading 工具)是一款专业、强大、高效的 SQLServer 数据库数据恢复工具。该工具由风哥研发,专为解决 SQLServer 数据库在各种极端场景下的数据丢失问题而设计。
当您的 SQLServer 数据库遇到以下情况时,FGSDU 可以为您提供强大的数据恢复能力:
- SQLServer 服务无法启动,包括 master 系统数据库损坏、实例崩溃、服务配置错误等导致数据库完全不可用的场景
- 数据库文件(MDF/NDF)存在磁盘坏块、部分损坏或文件系统错误
- 操作人员误执行 DELETE 语句删除了重要业务数据
- 开发或运维人员误执行 DROP TABLE 删除了整张表
- 误执行 TRUNCATE TABLE 清空了表中的全部数据
- 数据库经过 DBCC SHRINKFILE 收缩后仍有部分残留数据可恢复
- 需要在不启动 SQLServer 服务的情况下直接从数据文件中提取数据
- 需要对数据库文件进行取证分析或深度扫描
FGSDU 采用底层直接解析 MDF/NDF 数据文件的方式,完全不依赖 SQLServer 服务运行。工具通过深度解析 SQLServer 的 8KB 页结构、GAM/SGAM 分配位图、IAM 索引分配映射、系统表(sysobjects/syscolumns/systypes)元数据,以及数据页中的记录布局,能够在数据库完全不可用的情况下,最大限度地从原始数据文件中提取和恢复数据。
1.2 程序设计理念
FGSDU 的设计核心理念是"安全、高效、易用、可靠":
安全性第一:FGSDU 以只读方式打开所有 MDF/NDF 数据文件,绝对不会对原始数据库文件进行任何修改操作。这意味着您可以放心地在损坏的数据库文件上运行 FGSDU,而不必担心二次损坏。在进行任何恢复操作之前,我们仍强烈建议您先对原始文件进行完整备份。
最大化恢复率:工具采用多阶段、多层次的恢复算法,从常规恢复到深度扫描,层层递进,尽可能找出所有可恢复的数据。即使在系统表完全损坏无法读取的情况下,深度扫描模式仍能通过穷举所有页面的方式提取残留数据。
坏块自动容错:当数据文件存在磁盘坏块时,许多工具会直接报错退出,导致整个恢复过程失败。FGSDU 内置了完善的坏块处理机制,能够自动检测坏块、最多重试 3 次读取、跳过无法读取的页面并生成坏块报告,确保在磁盘损坏的情况下仍能最大程度地抢救可用数据。
数据完整性保障:导出的每一行数据都会经过 5 重完整性校验,包括列数一致性、NOT NULL 约束、数据类型匹配、长度限制和数值范围检查。有问题的行会在导出文件中用 WARNING 注释明确标记,让用户对恢复的数据质量一目了然。
内嵌恢复指导:所有导出的 SQL 文件和 DMP 文件配套说明中都内嵌了完整的 7 步数据恢复流程指南,即使是对 SQLServer 不熟悉的用户也能按照步骤一步步将恢复的数据导入新的数据库。
零依赖单文件部署:FGSDU 编译后为单文件可执行程序,无需安装任何运行库、无需配置环境变量、无需依赖任何第三方组件,复制到目标机器即可直接运行,极大地简化了在紧急恢复场景下的部署操作。
1.3 核心技术优势
FGSDU 相比其他同类工具,具有以下独特的技术优势:
-
深度页结构解析:完整支持 SQLServer 2005 至 2026 所有版本的页格式,包括数据页、索引页、LOB 页、IAM 页、GAM 页、SGAM 页、PFS 页等全部页类型的解析。
-
多阶段恢复算法:每种恢复类型(DELETE/DROP/TRUNCATE)都采用分阶段的恢复策略,结合多种技术手段综合恢复,比单一算法的恢复成功率更高。
-
无 Schema 推断能力:在系统表损坏无法获取表结构定义的情况下,工具能够通过分析记录的二进制布局自动推断列结构,生成 best-effort 的 INSERT 语句,最大程度挽救数据。
-
详细的进度输出:unload 全库抽取操作会实时显示每一张表的导出进度、导出行数、完成状态等详细信息,让用户清晰掌握恢复进展。
-
跨平台兼容性:通过 GLIBC 版本符号控制和 musl-libc 静态编译技术,Linux 版本可兼容从 RHEL 5 到 RHEL 10 的所有主流 Linux 发行版,同时提供 Windows 原生版本支持。
2. 作者信息
作者: 风哥
WX:itpux-com
官方网站 : http://www.fgedu.net.cn , http://www.itpux.com
数据库教程 : https://edu.51cto.com/lecturer/8020378.html
3. 功能特性
3.1 数据提取与导出功能
3.1.1 直接文件读取SQLServer
FGSDU 无需 SQLServer 服务运行,直接打开并解析 MDF(主数据文件)和 NDF(辅助数据文件)。工具绕过 SQLServer 引擎层,直接在文件系统层面读取和解析数据页,这使得它在数据库完全无法启动的灾难场景下依然能够正常工作。
支持同时加载多个数据文件,对于使用多个 NDF 辅助文件的大型数据库,可通过 -ndf 参数重复指定所有辅助文件,工具会统一管理多个文件的页空间,跨文件遍历页链。
3.1.2 双格式导出SQLServer
SQL 脚本格式(默认):
- 生成标准的 T-SQL 脚本文件,包含 CREATE TABLE 建表语句和 INSERT 数据插入语句
- 文件格式为纯文本,人类可读,可直接在 SQLServer Management Studio(SSMS)中打开和执行
- 也可通过 sqlcmd 命令行工具批量执行,便于自动化脚本集成
- 每条 INSERT 语句对应一行数据,方便逐行检查和筛选
- 文件头部内嵌完整的恢复步骤说明和数据完整性校验报告
- 有数据完整性问题的行会被特殊的 WARNING 注释标记
DMP 二进制格式:
- 采用紧凑的二进制存储格式,文件体积比 SQL 格式显著缩小
- 存储效率高,适合大批量数据导出场景
- 配套生成
.recovery_steps.txt恢复步骤说明文件,包含建表语句、导入指南和校验报告 - 支持 BULK INSERT、BCP 等多种高速导入方式
- 适合数据迁移、批量备份和自动化恢复流程
3.1.3 SQLServer全库一键抽取(unload)
当数据库无法启动时,unload 命令是最快捷的数据抢救方式。它具有以下特点:
- 自动遍历全库:无需手动指定表名,工具自动从系统元数据中识别所有可恢复的表
- 目录树组织:按"用户名/Schema_表名"的结构生成独立子目录,每张表一个独立文件,便于按表管理和恢复
- 按用户/Schema 过滤:支持通过
-user参数只导出特定用户拥有的表,或通过-schema参数只导出特定 Schema 下的表 - 汇总报告生成:自动生成
unload_report.txt汇总报告文件,包含每张表的导出状态(成功/失败)、列数、导出行数、输出路径、完整的恢复步骤说明和坏块报告 - 实时进度显示:导出过程中实时显示当前处理的表名、已导出行数、完成百分比等进度信息
- 坏块自动跳过:遇到坏块自动跳过并继续处理后续表,不会因为个别页面损坏而中断整个全库抽取过程
3.2 数据恢复功能
3.2.1 SQLServer DELETE 误删除数据恢复
恢复通过 DELETE 语句删除的行记录。
技术实现:
- Ghost 记录扫描:SQLServer 删除行时不会立即清除数据,而是将记录标记为 Ghost(幽灵记录),数据内容仍保留在页面中。FGSDU 扫描所有数据页,识别并提取这些标记为已删除的 Ghost 记录
- LSN 日志序列号分析:通过分析数据页头部的 LSN(Log Sequence Number,日志序列号),定位最近被修改过的页面,这些页面更可能包含刚被删除的数据
- 页链完整遍历:通过页头中的 prev/next 页指针遍历完整的页链,确保不遗漏链上任何一个页面的数据
- 智能去重处理:对从不同页面或不同扫描阶段提取到的重复记录进行自动去重,避免同一行数据被多次恢复
恢复成功率参考:
- 刚执行 DELETE(几分钟内):90%-95%
- 执行 DELETE 后 1 小时内:70%-90%
- 执行 DELETE 后数小时内:30%-70%
- 执行 DELETE 后数天:10%-30%
- 执行 DBCC SHRINKFILE 收缩后:低于 10%
3.2.2 SQLServer DROP TABLE 误删表恢复
恢复被 DROP TABLE 语句删除的整张表及其数据。
技术实现:
- sysobjects 残留扫描:在系统表中搜索被 DROP 表的元数据残留信息,即使表已被删除,系统表中可能仍保留有表名、ObjectID、列定义等元数据片段
- 孤立 IAM 页检测:识别不再被任何现有表引用的孤立 IAM(Index Allocation Map,索引分配映射)页面,这些 IAM 页是被 DROP 表留下的"脚印"
- ObjectID 关联映射:通过页头中的 ObjectID 字段将孤立的数据页归类到对应的被删除表
- 页链遍历重建:从检测到的起始数据页开始,通过 prev/next 指针遍历完整页链,恢复表的全部数据页
- Schema 自动推断:当元数据完全丢失无法获取表结构时,工具会分析记录二进制布局,自动推断列的数量、类型和长度,生成通用列名(col_1、col_2 等),导出 best-effort 的 INSERT 语句
3.2.3 SQLServer TRUNCATE TABLE 清空恢复
恢复被 TRUNCATE TABLE 语句清空的表数据。
TRUNCATE 操作的恢复难度最高,因为它会立即释放表占用的所有数据页,并更新 GAM/SGAM 位图标记这些区为可用。但在释放的页被新数据覆盖之前,原有的数据内容仍然保留在磁盘上。
技术实现:
- GAM/SGAM 位图解析:解析 GAM(Global Allocation Map,全局分配映射)页和 SGAM(Shared Global Allocation Map,共享全局分配映射)页,识别哪些区被标记为已释放
- 释放区穷举扫描:扫描所有被标记为释放的区,检查其中的页面是否仍包含有效的残留数据
- 高水位标记定位:通过分析分配映射的历史状态,定位 TRUNCATE 操作前的高水位标记,确定原表数据页的分布范围
- 残留页数据提取:从已释放但数据内容尚未被覆盖的页面中提取完整的记录数据
3.2.4 SQLServer全量恢复(recover all)
一键按顺序执行所有三种恢复类型:DELETE 恢复 → DROP 恢复 → TRUNCATE 恢复。适用于不确定数据是如何丢失的、或数据库遭受过多种操作的综合场景。
3.2.5 深度扫描(recover deep)
当常规恢复方法无法找到足够数据时使用的终极恢复手段。
深度扫描策略:
- 穷举扫描 MDF/NDF 文件中的每一个页面,不限制页类型
- 检查所有类型的页面:数据页、LOB 大对象页、索引页、甚至系统页
- 恢复所有状态的记录:正常记录、Ghost 已删除记录、Forwarded 转发记录
- 有完整 Schema 信息时:完整解码记录字段,输出标准 INSERT 语句
- 无 Schema 信息时:自动推断列结构,输出 best-effort INSERT 语句
- 同时输出 Raw Hex 原始十六进制数据作为取证备份
- 生成详细的扫描统计报告
深度扫描模式特别适用于以下场景:
- 数据库严重损坏,系统表完全不可读
- 常规 DELETE/DROP/TRUNCATE 恢复找不到数据
- 需要最大化数据恢复率,不惜以更长扫描时间为代价
- 司法取证场景,需要尽可能提取所有残留数据痕迹
3.3 坏块处理机制
FGSDU 内置了完善的坏块(Bad Block)处理机制,确保在磁盘存在物理损坏时仍能最大程度恢复可用数据。
处理流程:
-
坏块自动检测:读取页面时自动检测以下异常情况:
- 底层磁盘 I/O 读取失败
- 页面数据全部为零(典型的磁盘坏块特征)
- 页头中的页类型字段无效(不在合法范围 1-14 内)
- 页头中的页 ID 与实际读取位置的预期页 ID 不匹配
-
3 次自动重试:遇到瞬时性的 I/O 错误时,自动进行最多 3 次重试读取,排除偶发的读取干扰
-
跳过并记录:确认页面确实损坏无法读取后,跳过该页面,同时将坏块的页号、文件索引、错误类型和跳过次数记录到坏块列表中
-
继续恢复流程:跳过坏块后不中断整体恢复流程,继续扫描和处理其他所有正常页面
-
坏块报告生成:在恢复完成后,将坏块详情写入导出文件末尾和控制台输出,包括每个坏块的页号、损坏原因和处理方式
3.4 数据完整性校验
FGSDU 在导出数据时自动执行 5 重完整性校验,确保恢复的数据质量可追溯。
5 重校验项目:
| 校验项 | 校验内容 | 违规示例 |
|---|---|---|
| 列数一致性 | 行记录中的字段数量是否与表定义的列数完全匹配 | 表定义 8 列,但行中只有 7 个字段值 |
| NOT NULL 约束 | 定义为 NOT NULL 的列是否出现了 NULL 值 | 主键列被恢复出 NULL 值 |
| 数据类型匹配 | 值的实际数据类型与列定义的类型是否兼容(允许同类型间的强制转换) | 整数列中出现非数字字符数据 |
| 长度违规检查 | 字符串、二进制等变长数据的实际长度是否超过列定义的 max_length 限制 | VARCHAR(50) 列中出现 100 字符的字符串 |
| 数值范围检查 | 数值型和日期型数据是否在合法范围内 | TINYINT 出现 300(超出 0-255 范围)、DATETIME 出现 9999 年以后的日期 |
校验结果输出位置:
- 控制台实时输出校验摘要(通过行数、失败行数、通过率)
- SQL 导出文件头部包含完整的校验报告和违规详情
- 有问题的 INSERT 语句前会附加
-- WARNING: Row XXX has integrity issues (...)注释 - DMP 格式的校验报告写入配套的
.recovery_steps.txt文件中
3.5 目标过滤功能
支持多种维度的精确过滤,避免恢复无关数据,提高恢复效率和结果的针对性。
过滤维度:
- 按表名过滤:
-table <表名>,只恢复指定表名的数据 - 按 Schema 过滤:
-schema <Schema名>,只恢复指定 Schema 下的数据(如 dbo、sales、hr、finance) - 按用户过滤:
-user <用户名>,只恢复指定数据库用户拥有的表
组合使用:以上过滤条件可以任意组合使用,例如同时指定 -schema sales -table orders 可精确恢复 sales.orders 这一张表。
3.6 其他高级功能
3.6.1 日志级别控制
通过 -log 参数控制输出详细程度:
-log 0(静默模式):仅输出错误信息和最终结果,适合自动化脚本调用-log 1(信息模式,默认):输出操作进度、扫描统计、阶段状态等常规信息-log 2(调试模式):输出详细的诊断信息,包括每页的解析结果、中间状态转换等,用于故障排查和问题定位
3.6.2 数据库分析工具集
除了核心的恢复功能,FGSDU 还提供了丰富的数据库结构分析命令:
scan:快速列出所有用户表及估计行数info:显示数据库文件的头部信息(大小、页大小、SQLServer 版本、兼容级别、恢复模式、排序规则等)query:查询特定表的详细列结构(列名、数据类型、长度、是否可空)stats:显示数据库整体统计信息(表数、各类型页数、估计总行数、数据大小等)analyze:深度分析数据库结构,生成包含页类型分布、碎片情况、Ghost 记录统计、孤立页检测等内容的综合分析报告
4. 支持环境
4.1 支持的操作系统
4.1.1 Windows 平台
FGSDU Windows 版本支持以下操作系统:
- Windows 7 SP1 及以上(x86_64)
- Windows 8 / Windows 8.1(x86_64)
- Windows 10 所有版本(x86_64)
- Windows 11 所有版本(x86_64)
- Windows Server 2008 R2 SP1 及以上
- Windows Server 2012 / 2012 R2
- Windows Server 2016
- Windows Server 2019
- Windows Server 2022
- Windows Server 2025 及后续版本
Windows 版本说明:
- 提供原生 Win32 可执行程序(fgsdu.exe)
- 静态编译版本无任何运行库依赖,直接双击或在命令行运行即可
- 建议以管理员身份运行,以确保对 MDF/NDF 文件有足够的读取权限
4.1.2 Linux 平台
FGSDU Linux 版本支持以下主流发行版:
红帽系发行版:
- Red Hat Enterprise Linux (RHEL) 5.x / 6.x / 7.x / 8.x / 9.x / 10.x
- Oracle Enterprise Linux (OEL) 5.x 及以上
- CentOS 5.x / 6.x / 7.x / 8.x / 9.x
- Rocky Linux 8.x / 9.x
- AlmaLinux 8.x / 9.x
- Fedora Linux 所有稳定版本
Debian 系发行版:
- Ubuntu 14.04 LTS / 16.04 LTS / 18.04 LTS / 20.04 LTS / 22.04 LTS / 24.04 LTS
- Debian GNU/Linux 8 (Jessie) 及以上所有稳定版本
- Linux Mint 17.x 及以上
- Pop!_OS 所有版本
SUSE 系发行版:
- SUSE Linux Enterprise Server (SLES) 11 SPx / 12 SPx / 15 SPx
- openSUSE Leap 42.x 及以上所有稳定版本
- openSUSE Tumbleweed 滚动发行版
国产操作系统:
- 银河麒麟操作系统(Kylin OS)桌面版和服务器版
- 统信操作系统(UOS / UnionTech OS)桌面版和服务器版
- 中标麒麟(NeoKylin)操作系统
- 深度操作系统(Deepin)
- 其他基于 glibc 的国产 Linux 发行版
Linux 兼容性说明:
- 默认动态编译版本通过版本脚本强制符号依赖于 GLIBC_2.2.5,可在 RHEL 5 及以上的所有 glibc 系统上运行
- 提供 musl-libc 完全静态编译版本,无任何外部依赖,可在任意 x86_64 Linux 系统上运行,包括最小化安装的 Alpine Linux
- 仅支持 x86_64(amd64)架构,不支持 32 位 x86 或 ARM 架构
4.2 支持的 SQLServer 版本
FGSDU 支持以下所有 SQLServer 版本生成的 MDF/NDF 数据文件:
- SQL Server 2005(包括 SP1/SP2/SP3/SP4)
- SQL Server 2008(包括 SP1/SP2/SP3/SP4)
- SQL Server 2008 R2(包括 SP1/SP2/SP3)
- SQL Server 2012(包括 SP1/SP2/SP3/SP4)
- SQL Server 2014(包括 SP1/SP2/SP3)
- SQL Server 2016(包括 SP1/SP2/SP3)
- SQL Server 2017(包括 CU1-CU3x 所有累积更新)
- SQL Server 2019(包括 CU1-CU2x 所有累积更新)
- SQL Server 2022(包括所有累积更新)
- SQL Server 2025(即将发布版本的预支持)
- SQL Server 2026(未来版本的预支持)
工具会自动从 MDF 文件头部检测 SQLServer 版本,并根据检测结果选择对应的页结构解析逻辑和系统表查询方式,无需用户手动指定版本。
4.3 系统资源要求
4.3.1 最低硬件配置
- CPU:x86_64 架构,单核 1.0GHz 及以上
- 内存:最低 512MB 可用内存(建议 1GB 以上)
- 磁盘空间:程序本身占用约 5MB。用于存放导出文件的可用磁盘空间建议为源 MDF 文件总大小的 1.5-2 倍
- 磁盘类型:建议使用 SSD 固态硬盘以获得更好的扫描性能;HDD 机械硬盘也可正常使用,但扫描速度较慢
4.3.2 推荐硬件配置
- CPU:x86_64 架构,4 核及以上
- 内存:4GB 及以上可用内存
- 磁盘空间:源 MDF 文件总大小的 2-3 倍可用空间
- 磁盘类型:NVMe SSD 或 SATA SSD,将源 MDF 文件和导出目标文件放在不同物理磁盘上可获得最佳性能
4.3.3 大型数据库配置建议
对于超过 100GB 的大型 MDF 文件,建议:
- 内存不少于 8GB
- 使用 SSD 存储存放 MDF 文件和导出文件
- 分表导出(使用
-table参数逐表导出),避免单次操作数据量过大 - 使用 DMP 二进制格式导出以减少磁盘占用
- 使用
-log 0静默模式减少控制台 I/O 开销
5. 程序安装与编译
5.1 获取程序
用户可以通过项目提供的源代码自行编译 FGSDU。项目采用纯 C 语言编写,无任何第三方依赖库,编译过程非常简单。
5.2 Linux 平台编译
5.2.1 标准编译(推荐)
标准编译生成的动态链接版本具有最佳的 GLIBC 兼容性,可在 RHEL 5 及以上的绝大多数 Linux 发行版上直接运行。
# 进入项目根目录
cd FGSDU
# 执行标准编译
make
# 编译完成后,产物位于 build/fgsdu
ls -lh build/fgsdu
# 验证版本信息
./build/fgsdu version
# 验证 GLIBC 兼容性(预期主要符号为 GLIBC_2.2.5)
readelf -V build/fgsdu | grep GLIBC
5.2.2 GLIBC 静态编译
生成完全静态链接的可执行文件,不依赖系统 GLIBC,适合需要最大可移植性的场景。
# CentOS/RHEL 需先安装静态库
sudo yum install glibc-static libstdc++-static -y
# Ubuntu/Debian 需先安装静态库
sudo apt install libc6-dev -y
# 静态编译
make static
# 验证无动态依赖(预期输出:not a dynamic executable)
ldd build/fgsdu
5.2.3 musl-libc 静态编译(完全可移植)
使用 musl-gcc 编译生成完全静态、零依赖的二进制文件,可在任意 x86_64 Linux 系统上运行,包括 Alpine Linux 等非 glibc 系统。
# Ubuntu/Debian 安装 musl 工具链
sudo apt install musl-tools -y
# CentOS/RHEL 安装 musl 工具链
sudo yum install musl-libc-devel -y
# 使用 musl-gcc 编译
make musl
# 验证产物
file build/fgsdu
# 预期输出:ELF 64-bit LSB executable, x86-64, statically linked, ...
5.2.4 Docker 构建(推荐用于跨平台部署)
通过项目提供的 Dockerfile,基于 Alpine Linux + musl-libc 构建完全可移植的静态二进制文件。这是生成跨发行版可执行文件的最推荐方式,无需在宿主机安装任何编译工具链。
# 确保系统已安装 Docker 并启动服务
# 构建 Docker 镜像并生成静态二进制
make docker
# 编译完成后,产物位于 build/fgsdu
# 该文件为完全静态链接,可复制到任何 x86_64 Linux 系统直接运行
或者手动执行 Docker 命令:
docker build -t fgsdu-builder .
docker run --rm -v $(pwd)/build:/output fgsdu-builder
5.2.5 调试版本编译
用于开发调试目的,包含调试符号且不进行编译优化。
make debug
5.2.6 编译产物安装
将编译好的程序安装到系统标准路径:
# 默认安装到 /usr/local/bin
sudo make install
# 或指定自定义安装路径
make install PREFIX=/opt/fgsdu
5.3 Windows 平台编译
5.3.1 交叉编译(在 Linux 上编译 Windows 版本)
在 Linux 主机上使用 MinGW 交叉编译工具链生成 Windows 原生可执行文件。
# Ubuntu/Debian 安装 MinGW 交叉编译器
sudo apt install mingw-w64 -y
# CentOS/RHEL 安装 MinGW 交叉编译器
sudo yum install mingw32-gcc -y
# 交叉编译 Windows 版本
make win
# 编译产物位于 build/win/fgsdu.exe
# 将该文件复制到 Windows 系统即可直接运行
ls -lh build/win/fgsdu.exe
5.3.2 Windows 原生编译
在 Windows 系统上,可使用以下方式编译:
- Visual Studio 命令提示符:使用项目提供的
scripts/build_win.bat批处理脚本 - MinGW/MSYS2 环境:在 MSYS2 shell 中执行标准 make 命令
5.4 编译目标速查
| 编译命令 | 生成版本 | 适用场景 |
|---|---|---|
make |
GLIBC 动态版(兼容 GLIBC 2.2.5+) | 绝大多数 Linux 系统默认使用 |
make static |
GLIBC 静态版 | 需要避开系统 GLIBC 版本差异 |
make musl |
musl-libc 完全静态版 | 极致可移植性,Alpine 等非 glibc 系统 |
make docker |
musl-libc 完全静态版(通过 Docker) | 宿主机无编译环境或需确保可移植性 |
make win |
Windows 原生 exe(静态) | Windows 平台使用 |
make debug |
调试版本(含符号,无优化) | 开发调试和问题定位 |
6. 程序使用
6.1 基本语法
FGSDU 的命令行基本语法格式如下:
fgsdu <命令> [MDF文件路径] [选项参数]
其中:
<命令>:必填,指定要执行的操作,如 scan、export、unload、recover 等[MDF文件路径]:必填(help 和 version 命令除外),指定主数据文件(.mdf)的路径[选项参数]:选填,通过-参数名 值的形式指定额外选项
6.2 快速入门示例
以下是一组最常用的快速入门命令,建议首次使用时按顺序尝试:
# 1. 查看帮助信息,熟悉所有可用命令
./build/fgsdu help
# 2. 查看当前程序版本
./build/fgsdu version
# 3. 查看 MDF 文件的基本信息(验证文件可读且格式有效)
./build/fgsdu info /path/to/database.mdf
# 4. 列出 MDF 文件中包含的所有用户表
./build/fgsdu scan /path/to/database.mdf
# 5. 查看指定表的列结构定义
./build/fgsdu query /path/to/database.mdf orders
# 6. 导出指定表为 SQL 脚本(含恢复步骤和校验报告)
./build/fgsdu export /path/to/database.mdf orders -o orders.sql
# 7. 全库一键抽取(数据库无法启动时,推荐方式)
./build/fgsdu unload /path/to/database.mdf -o /backup/unload_all
# 8. 恢复误 DELETE 删除的数据
./build/fgsdu recover delete /path/to/database.mdf -o deleted_recovery.sql
# 9. 恢复误 DROP TABLE 删除的表
./build/fgsdu recover drop /path/to/database.mdf -o dropped_recovery.sql
# 10. 恢复误 TRUNCATE TABLE 清空的数据
./build/fgsdu recover truncate /path/to/database.mdf -o truncate_recovery.sql
6.3 全局选项参数说明
以下选项参数几乎可用于所有命令:
| 参数 | 简写 | 说明 | 示例 |
|---|---|---|---|
-ndf <文件路径> |
无 | 添加 NDF 辅助数据文件,可重复使用多次指定多个 NDF 文件 | -ndf data1.ndf -ndf data2.ndf |
-table <表名> |
无 | 指定目标表名,用于精确过滤恢复/导出 | -table orders |
-schema <Schema名> |
无 | 指定目标 Schema 名,如 dbo、sales、hr 等 | -schema sales |
-user <用户名> |
无 | 指定目标数据库用户名,用于按用户过滤 | -user fgedu |
-o <输出路径> |
无 | 指定输出文件或输出目录路径 | -o output.sql |
-format <格式> |
无 | 输出格式:sql(默认)或 dmp | -format dmp |
-log <级别> |
无 | 日志级别:0=静默,1=信息(默认),2=调试 | -log 2 |
-h 或 --help |
-h |
显示当前命令的帮助信息 | -h |
-v 或 --version |
-v |
显示程序版本号(与 version 命令等效) | -v |
7. 命令详解
7.1 信息查询类命令
7.1.1 help - 显示帮助信息
显示完整的命令列表、参数说明和使用示例。
fgsdu help
fgsdu ? # 别名
fgsdu recover help # 显示 recover 子命令的帮助
7.1.2 version - 显示版本信息
fgsdu version
fgsdu ver # 别名
fgsdu -v # 简写
7.1.3 info - 显示数据库文件信息
读取 MDF 文件头,显示数据库的基本属性信息,包括文件大小、页大小、总页数、SQLServer 版本、兼容级别、恢复模式、排序规则等。
fgsdu info mydb.mdf
fgsdu fileinfo mydb.mdf # 别名
fgsdu info primary.mdf -ndf secondary.ndf # 带 NDF 文件
输出示例说明:
- File:文件名
- Size:文件总大小(KB / MB / GB)
- Page Size:页大小,固定为 8192 字节(8KB)
- Total Pages:文件总页数(= 文件大小 / 8KB)
- File ID:文件 ID,MDF 通常为 1,NDF 从 2 开始递增
- Database ID:数据库 ID
- SQLServer Ver:检测到的 SQLServer 版本号
- Compatibility:数据库兼容级别
- Recovery Mode:恢复模式(FULL / BULK_LOGGED / SIMPLE)
- Collation:数据库默认排序规则
7.1.4 scan - 列出所有表
遍历系统表,列出 MDF 文件中所有可识别的用户表及其基本信息。
fgsdu scan mydb.mdf
fgsdu ls mydb.mdf # 别名
fgsdu list mydb.mdf # 别名
输出包含:序号、Schema.表名、Object ID、估计行数。
7.1.5 query - 查询表结构
查询指定表的详细列定义。
fgsdu query mydb.mdf orders # 查询 orders 表结构
fgsdu qry mydb.mdf orders # 别名
fgsdu query mydb.mdf # 不指定表名时列出所有表的结构
输出包含:列序号、列名、数据类型、最大长度、是否可空。
7.1.6 stats - 数据库统计信息
显示数据库整体的统计汇总信息。
fgsdu stats mydb.mdf
fgsdu st mydb.mdf # 别名
输出包含:表总数、总页数、各类页(数据页/索引页/IAM页/系统页)的数量、Ghost 记录数、估计总行数、数据总大小。
7.1.7 analyze - 深度结构分析
执行完整的数据库结构深度分析,生成详细报告。相比 stats 命令,analyze 会扫描所有页面,提供更全面的分析结果,包括页类型分布、碎片情况、孤立页检测、潜在问题识别等。
fgsdu analyze mydb.mdf
fgsdu ana mydb.mdf # 别名
fgsdu analyze mydb.mdf -log 2 # 调试模式输出更详细信息
7.2 数据导出类命令
7.2.1 export - 导出表数据
导出指定表的数据为 SQL 或 DMP 格式。
# 基本用法:导出为 SQL(自动生成文件名 <table>.sql)
fgsdu export mydb.mdf orders
# 指定输出文件路径
fgsdu export mydb.mdf orders -o backup/orders.sql
fgsdu exp mydb.mdf orders -o backup/orders.sql # 别名 exp
fgsdu dump mydb.mdf orders -o backup/orders.sql # 别名 dump
# 导出为 DMP 二进制格式
fgsdu export mydb.mdf orders -format dmp -o backup/orders.dmp
# 导出所有表到单个文件
fgsdu export mydb.mdf all -o all_tables.sql
# 导出多个表(逗号分隔)
fgsdu export mydb.mdf "orders,customers,products" -o multi_tables.sql
# 带 NDF 文件导出
fgsdu export primary.mdf -ndf data.ndf orders -o orders.sql
7.2.2 unload - 全库一键抽取
数据库无法启动时的首选命令,自动遍历所有表,按用户名/Schema 生成目录树结构,每张表导出为独立文件。
# 全库抽取所有表(推荐用于数据库无法启动场景)
fgsdu unload production.mdf -o /backup/unload_all
fgsdu extract production.mdf -o /backup/unload_all # 别名 extract
# 按用户名过滤,只导出指定用户的表
fgsdu unload production.mdf -user fgedu -o /backup/unload_fgedu
# 按 Schema 过滤,只导出指定 Schema 的表
fgsdu unload production.mdf -schema sales -o /backup/unload_sales
# 导出为 DMP 二进制格式
fgsdu unload production.mdf -user fgedu -format dmp -o /backup/unload_dmp
# 带多个 NDF 文件的全库抽取
fgsdu unload primary.mdf -ndf data1.ndf -ndf data2.ndf -o /backup/unload_multi
unload 生成的目录结构示例:
unload_fgedu/
├── dbo_orders/
│ └── orders.sql # 每张表的 SQL 导出文件
├── dbo_customers/
│ └── customers.sql
├── sales_orders/
│ └── orders.sql
├── hr_employees/
│ └── employees.sql
└── unload_report.txt # 汇总报告(必看!)
unload_report.txt 包含的内容:
- 导出基本信息:导出时间、源文件、过滤条件、导出格式
- 逐表导出状态列表:表名、状态(OK/FAIL)、列数、导出行数、输出文件路径
- 汇总统计:成功表数、失败表数、总导出数据行数、跳过的坏块数
- 完整的数据恢复步骤(7 步导入指南)
- 坏块详情报告(如有坏块)
7.3 数据恢复类命令
recover 命令是 FGSDU 的核心功能,包含 5 个子命令:
| 子命令 | 说明 |
|---|---|
recover delete |
恢复 DELETE 删除的行 |
recover drop |
恢复 DROP TABLE 删除的表 |
recover truncate |
恢复 TRUNCATE TABLE 清空的数据 |
recover all |
按顺序执行以上三种恢复 |
recover deep |
深度穷举扫描,最大化恢复率 |
7.3.1 recover delete - DELETE 数据恢复
# 恢复所有被 DELETE 的行
fgsdu recover delete production.mdf -o deleted.sql
fgsdu rec delete production.mdf -o deleted.sql # 别名 rec
# 只恢复指定表的已删除行
fgsdu recover delete production.mdf -table orders -o orders_deleted.sql
# 只恢复指定 Schema 的已删除行
fgsdu recover delete production.mdf -schema sales -o sales_deleted.sql
# 按用户+Schema+表名 组合精确过滤
fgsdu recover delete production.mdf -user admin -schema sales -table orders -o targeted.sql
# 导出为 DMP 格式
fgsdu recover delete production.mdf -format dmp -o deleted.dmp
7.3.2 recover drop - DROP TABLE 表恢复
# 恢复所有被 DROP 的表
fgsdu recover drop production.mdf -o dropped.sql
fgsdu rec drop production.mdf -o dropped.sql # 别名
# 按 Schema 过滤恢复
fgsdu recover drop production.mdf -schema hr -o hr_dropped.sql
# 按用户过滤恢复
fgsdu recover drop production.mdf -user dbadmin -o admin_dropped.sql
# 组合过滤
fgsdu recover drop production.mdf -user hr_owner -schema hr -o hr_owner_dropped.sql
# 调试模式查看详细扫描过程
fgsdu recover drop production.mdf -o dropped.sql -log 2
7.3.3 recover truncate - TRUNCATE 数据恢复
# 恢复所有 TRUNCATE 清空的数据
fgsdu recover truncate production.mdf -o truncate.sql
fgsdu rec truncate production.mdf -o truncate.sql # 别名
# 指定表名恢复
fgsdu recover truncate production.mdf -table orders -o orders_truncated.sql
# 指定 Schema 和表名
fgsdu recover truncate production.mdf -schema sales -table orders -o sales_orders.sql
# 带 NDF 文件
fgsdu recover truncate primary.mdf -ndf data.ndf -o truncate.sql
7.3.4 recover all - 全量恢复
按顺序执行 DELETE → DROP → TRUNCATE 三种恢复操作。
# 执行所有类型的恢复
fgsdu recover all production.mdf -o full_recovery.sql
fgsdu rec all production.mdf -o full_recovery.sql # 别名
# 带 Schema 过滤的全量恢复
fgsdu recover all production.mdf -schema sales -o sales_recovery.sql
# 带用户+Schema 组合过滤
fgsdu recover all production.mdf -user hr_owner -schema hr -o hr_recovery.sql
# 带多个 NDF 文件
fgsdu recover all primary.mdf -ndf d1.ndf -ndf d2.ndf -o recovery.sql
# 导出为 DMP 格式
fgsdu recover all production.mdf -format dmp -o full_recovery.dmp
7.3.5 recover deep - 深度扫描恢复
穷举扫描 MDF/NDF 文件中的每一个页面,最大化数据恢复率。
# 完整深度扫描
fgsdu recover deep production.mdf -o deep_recovery.sql
fgsdu rec deep production.mdf -o deep_recovery.sql # 别名
# 带过滤条件的深度扫描
fgsdu recover deep production.mdf -schema sales -o sales_deep.sql
fgsdu recover deep production.mdf -user admin -table orders -o targeted_deep.sql
# 调试模式输出详细信息
fgsdu recover deep production.mdf -o deep.sql -log 2
# 多文件深度扫描
fgsdu recover deep primary.mdf -ndf d1.ndf -ndf d2.ndf -o deep.sql
8. SQLServer数据恢复案例场景与操作过程
8.1 案例一:SQLServer生产数据库无法启动,全库紧急抢救
场景描述:
某企业 SQLServer 生产数据库因意外断电导致 master 系统数据库损坏,SQLServer 服务无法启动,业务完全中断。运维人员紧急联系数据库管理员,要求在最短时间内恢复所有业务数据。DBA 尝试多种方法修复 master 库均失败,决定使用 FGSDU 直接从 MDF 文件中提取数据。
操作过程:
步骤 1:立即停止操作并备份原始文件(至关重要!)
# 如果 SQLServer 服务还在异常运行,先强制停止
sudo systemctl stop mssql-server
# 将 MDF 和 NDF 文件复制到备份目录,所有操作都在副本上进行
# 假设数据文件位于 /var/opt/mssql/data/
cp /var/opt/mssql/data/production.mdf /backup/raw/production.mdf
cp /var/opt/mssql/data/production_log.ldf /backup/raw/production_log.ldf
cp /var/opt/mssql/data/production_data2.ndf /backup/raw/production_data2.ndf
# 对备份文件设置只读权限,防止误操作修改
chmod 444 /backup/raw/*
步骤 2:验证 MDF 文件可读取
# 使用 info 命令检查文件头是否有效
./fgsdu info /backup/raw/production.mdf -ndf /backup/raw/production_data2.ndf
预期输出显示正常的文件信息,包括 SQLServer 版本、页大小、总页数等。如果此步骤报错,说明文件头部严重损坏,需直接跳到深度扫描。
步骤 3:列出所有表,确认表结构可识别
./fgsdu scan /backup/raw/production.mdf -ndf /backup/raw/production_data2.ndf
记录输出的表列表,确认所有重要业务表都在列表中。
步骤 4:使用 unload 全库抽取(最快方式)
# 创建带时间戳的输出目录
OUTDIR="/backup/recovery/$(date +%Y%m%d_%H%M%S)_unload"
mkdir -p "$OUTDIR"
# 全库抽取,按 Schema 生成目录树
./fgsdu unload /backup/raw/production.mdf \
-ndf /backup/raw/production_data2.ndf \
-o "$OUTDIR" \
2>&1 | tee "$OUTDIR/unload_console.log"
导出过程中会实时显示每张表的进度:
Unloading table: dbo.orders ... OK (15230 rows)
Unloading table: dbo.customers ... OK (5120 rows)
Unloading table: dbo.order_items ... OK (45670 rows)
...
Unload complete: 25 tables OK, 0 tables FAILED, 78932 total rows exported
步骤 5:查看汇总报告
cat "$OUTDIR/unload_report.txt"
重点查看报告中的:
- Summary 部分确认所有表导出成功
- Bad Block Report 检查是否有坏块
- Recovery Steps 部分了解导入步骤
步骤 6:在新 SQLServer 实例上恢复数据
# 在新服务器上创建恢复数据库
sqlcmd -S newserver -E -Q "CREATE DATABASE recovered_prod;"
# 批量导入所有 SQL 文件
for sqlfile in "$OUTDIR"/*/*.sql; do
echo "Importing: $sqlfile"
sqlcmd -S newserver -E -d recovered_prod -i "$sqlfile"
done
# 验证表数量
sqlcmd -S newserver -E -d recovered_prod -Q "SELECT COUNT(*) AS TableCount FROM sys.tables;"
# 抽样验证几张关键表的行数
sqlcmd -S newserver -E -d recovered_prod -Q "
SELECT 'orders' AS TableName, COUNT(*) AS Rows FROM dbo.orders
UNION ALL
SELECT 'customers', COUNT(*) FROM dbo.customers
UNION ALL
SELECT 'order_items', COUNT(*) FROM dbo.order_items;
"
步骤 7:备份恢复后的数据库
sqlcmd -S newserver -E -Q "
BACKUP DATABASE recovered_prod
TO DISK = '/backup/recovered_prod_full.bak'
WITH FORMAT, COMPRESSION, STATS=10;
"
8.2 案例二:业务人员误 DELETE 删除SQLServer订单数据
场景描述:
某业务人员在清理测试数据时,忘记加 WHERE 条件直接执行了 DELETE FROM sales.orders,导致 50000 多条有效订单被全部删除。发现后立即停止了应用程序的写入操作。
操作过程:
步骤 1:立即停止所有写入操作
# 停止应用服务器(示例,根据实际部署调整)
sudo systemctl stop business-app
# 或在数据库层面设置数据库为只读
sqlcmd -S dbserver -E -Q "ALTER DATABASE salesdb SET READ_ONLY WITH ROLLBACK IMMEDIATE;"
步骤 2:立即备份 MDF 文件
# 先分离数据库(如果 SQLServer 还能访问)
sqlcmd -S dbserver -E -Q "
USE master;
GO
ALTER DATABASE salesdb SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
EXEC sp_detach_db 'salesdb';
GO
"
# 复制 MDF 和 NDF 文件
cp /var/opt/mssql/data/salesdb.mdf /backup/raw/salesdb.mdf
cp /var/opt/mssql/data/salesdb_data.ndf /backup/raw/salesdb_data.ndf
# 操作完毕后可重新附加数据库恢复服务
步骤 3:精确恢复 sales.orders 表的已删除行
# 步骤 3A:先尝试精确恢复指定 Schema 和表
./fgsdu recover delete /backup/raw/salesdb.mdf \
-ndf /backup/raw/salesdb_data.ndf \
-schema sales \
-table orders \
-o /backup/recovery/sales_orders_deleted.sql \
2>&1 | tee /backup/recovery/step3a.log
查看输出中的"Ghost rows extracted"和"Total rows recovered"数字,确认恢复的行数是否接近预期。
步骤 4:如果精确恢复数据量不足,执行全表 DELETE 恢复
./fgsdu recover delete /backup/raw/salesdb.mdf \
-ndf /backup/raw/salesdb_data.ndf \
-o /backup/recovery/all_deleted.sql \
2>&1 | tee /backup/recovery/step4.log
步骤 5:如果仍有缺失,执行深度扫描
./fgsdu recover deep /backup/raw/salesdb.mdf \
-ndf /backup/raw/salesdb_data.ndf \
-schema sales \
-table orders \
-o /backup/recovery/sales_orders_deep.sql \
2>&1 | tee /backup/recovery/step5.log
步骤 6:在测试数据库中验证恢复数据
# 创建测试数据库
sqlcmd -S testserver -E -Q "CREATE DATABASE recovery_test;"
# 导入恢复的 SQL 文件
sqlcmd -S testserver -E -d recovery_test -i /backup/recovery/sales_orders_deleted.sql
# 验证行数
sqlcmd -S testserver -E -d recovery_test -Q "SELECT COUNT(*) FROM sales.orders;"
# 抽样检查数据质量(查找有 WARNING 标记的问题行)
grep "WARNING" /backup/recovery/sales_orders_deleted.sql | head -20
步骤 7:将恢复的数据合并回生产库
确认数据无误后,由业务方确认,使用 INSERT INTO ... SELECT 将恢复的数据合并回生产表。
8.3 案例三:开发人员误 DROP TABLE 删除SQLServer员工表
场景描述:
某开发人员在生产环境执行脚本时,误将 DROP TABLE hr.employees 语句带到了生产库执行,导致包含 20000 多条员工信息的表被删除。HR 部门紧急要求恢复数据。
操作过程:
步骤 1:立即停止数据库操作
# 将数据库设为单用户模式,防止任何进一步的写入
sqlcmd -S dbserver -E -Q "
ALTER DATABASE hrdb SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
"
# 或直接停止 SQLServer 服务
sudo systemctl stop mssql-server
步骤 2:备份 MDF/NDF 文件
cp /data/hrdb.mdf /backup/raw/hrdb.mdf
cp /data/hrdb_data.ndf /backup/raw/hrdb_data.ndf
chmod 444 /backup/raw/*
步骤 3:使用 recover drop 恢复被 DROP 的表
# 按 hr Schema 过滤恢复
./fgsdu recover drop /backup/raw/hrdb.mdf \
-ndf /backup/raw/hrdb_data.ndf \
-schema hr \
-o /backup/recovery/hr_dropped.sql \
2>&1 | tee /backup/recovery/drop_restore.log
查看输出中的关键信息:
DROP Recovery Scan Complete:
Orphan pages found: 280
Found 1 potential dropped table(s)
Table 1 (ObjectID: 123456):
Data pages: 250
Estimated rows: 21500
Confidence: 82%
Extracting data... extracted 21487 rows
Recovery export completed: hr_dropped.sql
Total rows recovered: 21487
步骤 4:如果 Schema 过滤恢复不完整,执行全局 DROP 恢复
./fgsdu recover drop /backup/raw/hrdb.mdf \
-ndf /backup/raw/hrdb_data.ndf \
-o /backup/recovery/all_dropped.sql
步骤 5:终极手段 - 深度扫描
./fgsdu recover deep /backup/raw/hrdb.mdf \
-ndf /backup/raw/hrdb_data.ndf \
-o /backup/recovery/hr_deep.sql -log 2
步骤 6:验证并导入恢复的数据
# 在测试库中创建 hr Schema
sqlcmd -S testserver -E -d TestDB -Q "CREATE SCHEMA hr;"
# 导入恢复的 SQL
sqlcmd -S testserver -E -d TestDB -i /backup/recovery/hr_dropped.sql
# 检查表是否存在及行数
sqlcmd -S testserver -E -d TestDB -Q "
SELECT name, type_desc, create_date
FROM sys.tables
WHERE schema_id = SCHEMA_ID('hr');
SELECT COUNT(*) AS EmpRows FROM hr.employees;
"
# 抽样检查员工数据
sqlcmd -S testserver -E -d TestDB -Q "SELECT TOP 10 * FROM hr.employees ORDER BY emp_id;"
步骤 7:业务方确认后恢复到生产库
由 HR 部门核对员工数据无误后,DBA 将表导回生产数据库,并重建索引、约束和权限。
8.4 案例四:误 TRUNCATE 清空SQLServer订单明细表
场景描述:
运维人员在清理历史数据时,误对生产表执行了 TRUNCATE TABLE sales.order_items,导致 450000 多条订单明细数据被清空。TRUNCATE 操作执行极快,发现时操作已完成。
操作过程:
步骤 1:立即停止所有写入(TRUNCATE 已释放页面,必须争分夺秒!)
# 立即停止应用和 SQLServer 服务
sudo systemctl stop business-app
sudo systemctl stop mssql-server
注意:TRUNCATE 会立即将所有数据页标记为"已释放",如果此时有新数据写入数据库,这些已释放的页面随时可能被分配给新数据,导致原始数据被永久覆盖。时间越短,恢复成功率越高!
步骤 2:备份文件
# 复制所有数据文件
cp /data/salesdb.mdf /backup/raw/salesdb.mdf
cp /data/salesdb_idx.ndf /backup/raw/salesdb_idx.ndf
cp /data/salesdb_archive.ndf /backup/raw/salesdb_archive.ndf
步骤 3:TRUNCATE 恢复
# 指定 Schema 和表名精确恢复
./fgsdu recover truncate /backup/raw/salesdb.mdf \
-ndf /backup/raw/salesdb_idx.ndf \
-ndf /backup/raw/salesdb_archive.ndf \
-schema sales \
-table order_items \
-o /backup/recovery/order_items_truncated.sql \
2>&1 | tee /backup/recovery/truncate.log
步骤 4:执行全量恢复(包含 DELETE/DROP/TRUNCATE 全部算法)
如果单独 TRUNCATE 恢复的数据量不足,继续执行:
./fgsdu recover all /backup/raw/salesdb.mdf \
-ndf /backup/raw/salesdb_idx.ndf \
-ndf /backup/raw/salesdb_archive.ndf \
-schema sales \
-o /backup/recovery/sales_all.sql
步骤 5:深度扫描
./fgsdu recover deep /backup/raw/salesdb.mdf \
-ndf /backup/raw/salesdb_idx.ndf \
-ndf /backup/raw/salesdb_archive.ndf \
-schema sales \
-table order_items \
-o /backup/recovery/order_items_deep.sql
步骤 6:验证数据,去重后导入生产库
TRUNCATE 恢复的数据可能存在重复行,导入后需执行去重。
-- 导入后去重
;WITH CTE AS (
SELECT *, ROW_NUMBER() OVER (
PARTITION BY order_id, product_id -- 按实际主键或唯一列
ORDER BY (SELECT 0)
) AS rn
FROM sales.order_items
)
DELETE FROM CTE WHERE rn > 1;
GO
8.5 案例五:磁盘存在坏块的SQLServer数据库文件恢复
场景描述:
某数据库服务器的 RAID 卡出现故障,导致部分磁盘坏道。SQLServer 服务频繁报错,尝试 DBCC CHECKDB 修复失败,大量页面报告一致性错误。需要在磁盘彻底损坏前尽可能抢救数据。
操作过程:
步骤 1:创建磁盘镜像(建议优先使用 ddrescue)
# 使用 ddrescue 创建磁盘级镜像,跳过坏块
# ddrescue /dev/sdb1 /backup/raw/disk_image.img /backup/raw/rescue.log
# 如果没有 ddrescue,直接复制文件,遇到坏块操作系统会报错跳过
cp --sparse=always /data/production.mdf /backup/raw/production.mdf
cp --sparse=always /data/production.ndf /backup/raw/production.ndf
步骤 2:使用 FGSDU 全库抽取(自动跳过坏块)
./fgsdu unload /backup/raw/production.mdf \
-ndf /backup/raw/production.ndf \
-o /backup/recovery/unload_with_badblocks \
2>&1 | tee /backup/recovery/unload.log
执行过程中会看到类似警告:
Warning: Skipping bad block page 12345 (all-zero data)
Warning: Skipping bad block page 23456 (invalid page type: 0)
Note: Page 34567 read succeeded on retry 2
Warning: Skipping bad block page 45678 (Read failed after 3 attempts: IO error)
步骤 3:查看坏块报告
cat /backup/recovery/unload_with_badblocks/unload_report.txt
查看末尾的 Bad Block Report 部分:
Bad Block Report
========================================
Total bad blocks encountered: 5
Total pages skipped (with retries): 5
Strategy: All corrupted blocks were SKIPPED to maximize data recovery
Details:
Page 12345 (file 0): All-zero page data (disk corruption) (skipped 1 time)
Page 23456 (file 0): Invalid page type in header (skipped 1 time)
Page 45678 (file 0): Read failed after 3 attempts: IO error (skipped 1 time)
...
步骤 4:对导出失败的表单独尝试深度扫描
# 假设 unload_report 显示 dbo.big_table 导出失败或行数严重偏少
./fgsdu recover deep /backup/raw/production.mdf \
-ndf /backup/raw/production.ndf \
-schema dbo \
-table big_table \
-o /backup/recovery/big_table_deep.sql \
-log 2
步骤 5:执行全量恢复和深度扫描,尽量找回更多数据
./fgsdu recover all /backup/raw/production.mdf \
-ndf /backup/raw/production.ndf \
-o /backup/recovery/full_recovery.sql
./fgsdu recover deep /backup/raw/production.mdf \
-ndf /backup/raw/production.ndf \
-o /backup/recovery/deep_scan.sql
8.6 案例六:SQLServer多 Schema 数据库按业务分批恢复
场景描述:
某大型数据库包含多个业务 Schema(dbo、sales、hr、finance、inventory、crm),数据库损坏后需要按业务线的优先级分批恢复,优先恢复最关键的销售和财务数据。
操作过程:
#!/bin/bash
# batch_recovery_by_priority.sh - 按优先级分批恢复脚本
MDF="/backup/raw/production.mdf"
NDF1="/backup/raw/prod_data1.ndf"
NDF2="/backup/raw/prod_data2.ndf"
BASEOUT="/backup/recovery/$(date +%Y%m%d_%H%M%S)"
FGSDU="/opt/fgsdu/fgsdu"
echo "=== FGSDU 按优先级分批恢复 ==="
echo "开始时间: $(date)"
echo "输出根目录: $BASEOUT"
echo ""
# === 第一优先级:销售 (sales) 和 财务 (finance) ===
echo "[优先级 1/3] 恢复销售和财务数据..."
for SCHEMA in sales finance; do
OUTDIR="$BASEOUT/priority1_$SCHEMA"
mkdir -p "$OUTDIR"
echo " 恢复 Schema: $SCHEMA"
echo " - 全库抽取..."
$FGSDU unload "$MDF" -ndf "$NDF1" -ndf "$NDF2" \
-schema "$SCHEMA" -o "$OUTDIR/unload" > "$OUTDIR/unload.log" 2>&1
echo " - 全量恢复 (DELETE+DROP+TRUNCATE)..."
$FGSDU recover all "$MDF" -ndf "$NDF1" -ndf "$NDF2" \
-schema "$SCHEMA" -o "$OUTDIR/recovery_all.sql" > "$OUTDIR/recovery.log" 2>&1
echo " - 深度扫描..."
$FGSDU recover deep "$MDF" -ndf "$NDF1" -ndf "$NDF2" \
-schema "$SCHEMA" -o "$OUTDIR/deep_scan.sql" > "$OUTDIR/deep.log" 2>&1
done
# === 第二优先级:CRM 和 库存 (inventory) ===
echo ""
echo "[优先级 2/3] 恢复 CRM 和库存数据..."
for SCHEMA in crm inventory; do
OUTDIR="$BASEOUT/priority2_$SCHEMA"
mkdir -p "$OUTDIR"
echo " 恢复 Schema: $SCHEMA"
$FGSDU unload "$MDF" -ndf "$NDF1" -ndf "$NDF2" \
-schema "$SCHEMA" -o "$OUTDIR/unload" > "$OUTDIR/unload.log" 2>&1
$FGSDU recover all "$MDF" -ndf "$NDF1" -ndf "$NDF2" \
-schema "$SCHEMA" -o "$OUTDIR/recovery_all.sql" > "$OUTDIR/recovery.log" 2>&1
done
# === 第三优先级:HR 和 dbo ===
echo ""
echo "[优先级 3/3] 恢复 HR 和公共 dbo 数据..."
for SCHEMA in hr dbo; do
OUTDIR="$BASEOUT/priority3_$SCHEMA"
mkdir -p "$OUTDIR"
echo " 恢复 Schema: $SCHEMA"
$FGSDU unload "$MDF" -ndf "$NDF1" -ndf "$NDF2" \
-schema "$SCHEMA" -o "$OUTDIR/unload" > "$OUTDIR/unload.log" 2>&1
$FGSDU recover all "$MDF" -ndf "$NDF1" -ndf "$NDF2" \
-schema "$SCHEMA" -o "$OUTDIR/recovery_all.sql" > "$OUTDIR/recovery.log" 2>&1
done
echo ""
echo "=== 全部恢复完成 ==="
echo "结束时间: $(date)"
echo "所有文件在: $BASEOUT"
find "$BASEOUT" -type f -name "*.sql" -o -name "*.txt" -o -name "*.log" | sort
8.7 案例七:使用导出的 SQL 文件恢复到SQLServer新数据库(完整流程)
场景描述:
已使用 FGSDU 导出了若干 SQL 文件,现在需要将这些文件中的数据导入到新搭建的 SQLServer 数据库中。
操作过程:
步骤 1:阅读导出 SQL 文件头部的恢复步骤
每个由 FGSDU 导出的 SQL 文件头部都包含了完整的恢复步骤说明和校验报告。在执行前务必先阅读:
head -80 /backup/recovery/dbo_orders.sql
重点关注:
- 表结构定义(CREATE TABLE 语句)
- 数据完整性校验报告的通过率和违规详情
- 内嵌的 7 步恢复指南
步骤 2:创建目标数据库
-- 在 SSMS 中执行,或通过 sqlcmd
CREATE DATABASE recovered_database
ON PRIMARY (
NAME = recovered_database_data,
FILENAME = '/var/opt/mssql/data/recovered_database.mdf',
SIZE = 10GB,
MAXSIZE = 100GB,
FILEGROWTH = 1GB
)
LOG ON (
NAME = recovered_database_log,
FILENAME = '/var/opt/mssql/data/recovered_database_log.ldf',
SIZE = 2GB,
MAXSIZE = 50GB,
FILEGROWTH = 512MB
);
GO
ALTER DATABASE recovered_database SET RECOVERY SIMPLE;
GO
USE recovered_database;
GO
步骤 3:创建需要的 Schema
如果恢复的表不属于 dbo Schema,需要先手动创建 Schema:
CREATE SCHEMA sales;
GO
CREATE SCHEMA hr;
GO
CREATE SCHEMA finance;
GO
步骤 4:执行 SQL 文件导入数据
方式 A - 使用 SSMS:
- 打开 SQL Server Management Studio
- 连接到目标数据库实例
- 点击"文件" → "打开" → "文件",选择导出的 SQL 文件
- 确认当前数据库为 recovered_database
- 点击"执行"按钮(F5)运行脚本
方式 B - 使用 sqlcmd 命令行(推荐用于自动化和大文件):
# 基本用法
sqlcmd -S dbserver -E -d recovered_database -i /backup/recovery/dbo_orders.sql
# 带用户名密码(SQL Server 认证模式)
sqlcmd -S dbserver -U sa -P 'YourPassword' -d recovered_database \
-i /backup/recovery/dbo_orders.sql
# 设置大文件批次参数
sqlcmd -S dbserver -E -d recovered_database \
-i /backup/recovery/dbo_big_table.sql \
-b -o import_big_table.log
方式 C - 批量导入多个 SQL 文件(Shell 脚本):
#!/bin/bash
SQLDIR="/backup/recovery/unload_all"
SERVER="dbserver"
DB="recovered_database"
for sqldir in "$SQLDIR"/*/; do
sqlfile=$(find "$sqldir" -maxdepth 1 -name "*.sql" | head -1)
if [ -f "$sqlfile" ]; then
echo "Importing: $(basename $sqldir) -> $sqlfile"
sqlcmd -S "$SERVER" -E -d "$DB" -i "$sqlfile" \
-o "${sqlfile}.import.log"
if [ $? -eq 0 ]; then
echo " OK"
else
echo " FAILED - see ${sqlfile}.import.log"
fi
fi
done
步骤 5:验证数据导入结果
-- 1. 验证所有表都已创建
SELECT
SCHEMA_NAME(schema_id) AS SchemaName,
name AS TableName,
create_date
FROM sys.tables
ORDER BY SchemaName, TableName;
-- 2. 验证每张表的行数(与导出时对比)
SELECT
SCHEMA_NAME(t.schema_id) + '.' + t.name AS TableName,
p.rows AS RowCounts
FROM sys.tables t
INNER JOIN sys.partitions p ON t.object_id = p.object_id
WHERE p.index_id IN (0, 1)
ORDER BY TableName;
-- 3. 抽样检查数据质量
SELECT TOP 100 * FROM dbo.orders ORDER BY order_id DESC;
SELECT TOP 100 * FROM hr.employees ORDER BY emp_id;
-- 4. 检查是否有完整性问题的行(搜索 WARNING 标记)
-- 可在 SQL 文件中查找包含 WARNING 注释的 INSERT 行
步骤 6:去除重复行
恢复的数据(尤其是 DELETE 和深度扫描恢复的)可能包含重复行,需要去重:
-- 通用去重脚本(按实际主键修改 PARTITION BY 列)
DECLARE @TableName NVARCHAR(200) = 'dbo.orders';
DECLARE @KeyColumns NVARCHAR(500) = 'order_id'; -- 主键或唯一列
DECLARE @SQL NVARCHAR(MAX) = N'
;WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY ' + @KeyColumns + '
ORDER BY (SELECT 0)
) AS rn
FROM ' + @TableName + '
)
DELETE FROM CTE WHERE rn > 1;
';
EXEC sp_executesql @SQL;
PRINT 'Deduplication completed for ' + @TableName;
GO
步骤 7:重建索引和约束
FGSDU 导出的 SQL 通常只包含基本的 CREATE TABLE 和 INSERT,不包含索引、主键、外键等约束(因为恢复场景下表结构可能不完整)。数据验证无误后,需要根据原始数据库设计重建:
-- 示例:重建主键和索引
ALTER TABLE dbo.orders
ADD CONSTRAINT PK_orders PRIMARY KEY CLUSTERED (order_id);
GO
CREATE NONCLUSTERED INDEX IX_orders_customer_id
ON dbo.orders(customer_id);
GO
CREATE NONCLUSTERED INDEX IX_orders_order_date
ON dbo.orders(order_date);
GO
-- 重建外键
ALTER TABLE dbo.order_items
ADD CONSTRAINT FK_order_items_orders
FOREIGN KEY (order_id) REFERENCES dbo.orders(order_id);
GO
步骤 8:备份恢复后的数据库
-- 完整备份
BACKUP DATABASE recovered_database
TO DISK = '/backup/recovered_database_FULL.bak'
WITH
FORMAT,
COMPRESSION,
STATS = 5,
DESCRIPTION = 'Post-recovery full backup - FGSDU';
GO
9. 数据恢复原理说明
9.1 SQLServer 存储基础
SQLServer 数据库的物理存储基于以下核心概念:
页(Page):SQLServer 中最基本的 I/O 单元,固定大小为 8KB(8192 字节)。每个页都有一个 96 字节的页头,记录页类型、ObjectID、LSN、前后页指针等关键信息。
区(Extent):由 8 个连续的页组成,共 64KB。区是空间分配的基本单位。
GAM 页(Global Allocation Map):全局分配映射页,每 4GB 数据空间一个 GAM 页,用位图记录哪些区已被分配。
SGAM 页(Shared Global Allocation Map):共享全局分配映射页,记录哪些区是共享区(包含来自多个对象的页)。
IAM 页(Index Allocation Map):索引分配映射页,每个表或索引至少有一个 IAM 页,记录表占用的所有区和页。
系统表:
- sysobjects:存储所有数据库对象(表、视图、存储过程等)的元数据
- syscolumns:存储所有列的定义(列名、数据类型、长度、是否可空)
- systypes:存储数据类型定义
9.2 DELETE 恢复原理详解
第一阶段:LSN 事务日志扫描
扫描所有数据页的页头 LSN(日志序列号)。LSN 越大表示页面最近被修改的时间越近。通过定位高 LSN 的页面,可快速找出近期发生删除操作的热点页。
第二阶段:Ghost 记录扫描
SQLServer 执行 DELETE 时,并不会立即清除行数据,而是将行头的状态位标记为 Ghost Record(幽灵记录)。FGSDU 逐页扫描,识别状态位被标记为已删除但数据内容仍然完整的 Ghost 记录,将其完整提取出来。
第三阶段:页链遍历
通过每个数据页头中的 prevPage 和 nextPage 指针,从对象的第一个页开始,顺着双向链表遍历完整的页链。这样可以确保不遗漏链上任何一个页面的 Ghost 记录。
第四阶段:自动去重
由于同一行数据可能在多个阶段被多次检测到(例如 LSN 扫描和 Ghost 扫描都找到了),FGSDU 会根据 ObjectID + 主键列值 + 行内容哈希进行智能去重,确保最终输出的每一行都是唯一的。
9.3 DROP TABLE 恢复原理详解
第一阶段:sysobjects 残留扫描
当执行 DROP TABLE 时,SQLServer 会从 sysobjects 和 syscolumns 中删除表的元数据记录。但在存储层面,这些被删除的系统记录本身可能以 Ghost 记录的形式仍然残留在系统表数据页中。FGSDU 专门扫描系统页的 Ghost 记录,尝试找回被 DROP 表的原始元数据(表名、列定义等)。
第二阶段:孤立页检测与归类
遍历所有数据页,检查页头中的 ObjectID。如果某个 ObjectID 在当前的 sysobjects 中已经找不到对应的表定义,那么该页就是一个"孤立页",很可能属于被 DROP 的表。将具有相同 ObjectID 的孤立数据页归集到一起,就形成了候选的被删除表。
第三阶段:页链遍历重建
从检测到的每个 ObjectID 的起始页开始,通过 prev/next 页指针沿着双向链表遍历,将属于同一表的所有数据页完整串连起来,恢复表的完整数据页集合。
第四阶段:Schema 自动推断
如果无法通过系统表残留获取到表结构(即使用户已清空回收站或系统页也被覆盖),FGSDU 会启动 Schema 推断引擎:
- 从样本记录中解析记录头:固定长度、列数、NULL 位图、可变长度偏移数组
- 根据固定长度字段的字节宽度,匹配最可能的数据类型(如 4 字节匹配 INT,8 字节匹配 BIGINT/DATETIME/MONEY)
- 对可变长度字段,根据内容特征(是否全 ASCII、是否含 Unicode BOM、是否数字/日期格式)推断 VARCHAR/NVARCHAR 等类型
- 生成通用列名 col_1、col_2、col_3 等,并输出 best-effort 的 INSERT 语句
9.4 TRUNCATE 恢复原理详解
TRUNCATE TABLE 的执行过程是:立即释放表的所有区(更新 GAM/SGAM 位图标记为可用)、清空 IAM 页链、更新系统表中的页计数元数据。TRUNCATE 不逐行删除,因此不产生 DELETE 日志,也不会留下 Ghost 记录。但它也有一个弱点:只是在分配位图上"标记区为可用",并没有实际清除区中页面上的数据内容。在这些已释放的页面被 SQLServer 重新分配给新对象并写入新数据之前,原表的数据仍然完好地保存在磁盘上。
第一阶段:GAM/SGAM 位图解析
解析所有 GAM 页和 SGAM 页的位图,找出当前被标记为"未分配/可用"的区。这些被释放的区就是 TRUNCATE 操作后原表数据所在的位置候选集。
第二阶段:释放区穷举扫描
对第一步找出的所有已释放区中的每一个页面,逐一读取并检查页头。如果某个页面的页头仍然包含有效的 ObjectID、有效的页类型(DATA_PAGE)、并且 slot 数组不为空,说明该页面仍然包含残留的数据。
第三阶段:高水位定位与关联
通过分析已释放页面在文件中的物理分布规律,结合 IAM 页的残留痕迹,推断 TRUNCATE 前表的高水位标记(HWM,High Water Mark)位置,确定原表数据页的大致分布范围。
第四阶段:残留页数据提取
从所有包含有效数据的已释放页中,完整提取 slot 数组指向的每一条记录。由于此时通常已无元数据可用,提取的数据会配合 Schema 推断机制输出。
10. 常用问题与排查
10.1 程序运行类问题
Q1:运行 FGSDU 报错 "Error: Cannot open MDF file - Permission denied"
原因分析:当前操作系统用户对指定的 MDF/NDF 文件没有读取权限。这在从 SQLServer 默认数据目录(如 /var/opt/mssql/data/)读取时非常常见,因为这些目录通常只有 mssql 用户有读取权限。
解决方案:
# 方案 1:先将文件复制到当前用户可访问的目录,再操作(推荐!)
sudo cp /var/opt/mssql/data/mydb.mdf ~/
sudo cp /var/opt/mssql/data/mydb.ndf ~/
sudo chown $(whoami):$(whoami) ~/mydb.mdf ~/mydb.ndf
./fgsdu info ~/mydb.mdf
# 方案 2:使用 sudo 运行 FGSDU
sudo ./fgsdu info /var/opt/mssql/data/mydb.mdf
# 方案 3:临时添加文件读取权限(操作完改回去)
sudo chmod 644 /var/opt/mssql/data/mydb.mdf
./fgsdu info /var/opt/mssql/data/mydb.mdf
sudo chmod 600 /var/opt/mssql/data/mydb.mdf # 恢复原权限
Q2:报错 "Error: Invalid MDF file format" 或 "Bad magic number in file header"
原因分析:程序无法识别指定文件为有效的 SQLServer MDF 格式。可能的原因包括:
- 文件不是真正的 MDF 文件(例如误传了 LDF 日志文件、BAK 备份文件或其他文件)
- 文件头部被完全破坏(前 8KB 数据损坏)
- 文件在传输或复制过程中被截断(大小不完整)
排查步骤:
# 1. 确认文件扩展名和实际类型
file /path/to/file.mdf
# MDF 文件通常显示为:"data" 或 "MS SQL Server Database"
# 2. 检查文件前几个字节的十六进制(MDF 以特定签名开头)
xxd /path/to/file.mdf | head -5
# 3. 检查文件大小是否为 8KB 的整数倍
ls -l /path/to/file.mdf
# 文件大小 (bytes) / 8192 应该是整数,且余数为 0
# 4. 确认不是 LDF 日志文件
# LDF 文件的格式完全不同,FGSDU 不处理 LDF
解决方案:
- 如果误传了 LDF 或 BAK 文件,请提供正确的 MDF 主数据文件
- 如果文件被截断,请从原始位置重新复制完整文件
- 如果文件头完全损坏,尝试使用
recover deep深度扫描,即使头部损坏,文件后半部分的正常数据页仍可能被提取
Q3:Linux 运行时提示 "GLIBC_2.xx not found"
原因分析:当前系统的 GLIBC 版本低于编译 FGSDU 时使用的 GLIBC 版本。
排查步骤:
# 查看当前系统 GLIBC 版本
ldd --version
# 查看 FGSDU 所需的 GLIBC 符号
readelf -V ./fgsdu | grep GLIBC
解决方案:
# 方案 1(推荐):使用项目提供的 Docker 构建完全静态的 musl 版本
cd FGSDU
make docker
# build/fgsdu 为完全静态版本,无任何 GLIBC 依赖
# 方案 2:如果安装了 musl-gcc,直接编译
make musl
# 方案 3:在目标系统上本地重新编译
cd FGSDU
make clean && make
Q4:扫描或恢复速度非常慢,长时间没有输出
可能原因和对应解决方法:
| 原因 | 现象 | 解决方法 |
|---|---|---|
| MDF 文件很大(> 100GB)且使用机械硬盘 | 进度缓慢但持续前进 | 耐心等待;或将 MDF 迁移到 SSD;使用 -log 0 减少输出 |
| 磁盘存在大量坏块 | 频繁出现 "Retrying read" 提示 | 正常现象,坏块需要多次重试;先用 ddrescue 做磁盘镜像再操作 |
| 使用了深度扫描模式 | 进度条缓慢移动 | 正常现象,深度扫描会检查每一个页面的每一个字节;大文件可能需要数小时 |
| 操作系统缓冲区不足 | 磁盘 I/O 100% 但 CPU 使用率不高 | 增加系统内存;或调整 vm.dirty_ratio 等内核参数 |
| 输出文件写到了同一个慢速磁盘上 | 读写竞争导致瓶颈 | 将源 MDF 和输出文件放在不同物理磁盘上 |
性能优化建议:
# 1. 将输出重定向到 SSD 上的不同物理磁盘
./fgsdu recover deep /mnt/hdd/bigdb.mdf -o /mnt/ssd/output.sql
# 2. 使用静默模式减少控制台 I/O
./fgsdu recover all /mnt/hdd/bigdb.mdf -o output.sql -log 0
# 3. 导出为 DMP 格式减少写入量
./fgsdu unload bigdb.mdf -format dmp -o /mnt/ssd/output_dmp
10.2 数据恢复效果类问题
Q5:DELETE 恢复只找回了极少部分数据,大部分丢失的数据没找到
可能原因与排查步骤:
-
时间过久导致 Ghost 记录已被清理
- SQLServer 后台有 Ghost Cleanup 线程会定期清理已删除的记录
- 删除操作后经过数小时或数据库活动频繁,Ghost 记录大概率已被清理
- 解决方法:尝试
recover deep深度扫描
-
删除后数据库经过了收缩(DBCC SHRINKFILE)
- 收缩操作会移动和重写页面,彻底覆盖已释放的页面
- 这是导致 DELETE 恢复失败的最主要原因之一
- 解决方法:深度扫描可能还能找到未被覆盖的零散页面,但恢复率会显著降低
-
已删除的页面被新数据重用了
- 如果 DELETE 后有大量 INSERT/UPDATE 操作,原来的空闲页会被分配给新数据
- 解决方法:确认删除后是否立即停止了写入;如果没有,尽快尝试深度扫描
-
过滤条件不正确
- 指定的
-schema或-table名称与实际不符(注意大小写敏感设置) - 解决方法:先不加过滤条件运行
recover delete,看输出中是否能找到目标表
- 指定的
恢复顺序建议:
精确 recover delete (带-schema/-table)
↓ 不够
全量 recover delete (不带过滤)
↓ 不够
recover all (DELETE+DROP+TRUNCATE)
↓ 不够
recover deep (终极方案)
Q6:DROP TABLE 恢复后表名显示为 ObjectID 或 inferred_table,列名显示为 col_1、col_2
原因分析:DROP 恢复找不到被删表的元数据(sysobjects/syscolumns 残留),只能通过孤立页检测。此时没有表名和列名信息,FGSDU 只能用:
- ObjectID 作为临时表名(如 ObjectID_123456)
- col_1、col_2、col_3 等作为推断列名
这是正常现象,不代表恢复失败。数据内容本身是正确的,只是缺少了人类可读的名称。
解决方案:
/* 在导入到测试数据库后,根据业务知识手动重命名 */
-- 1. 先检查表中的推断列数据
SELECT TOP 50 * FROM dbo.ObjectID_123456;
-- 2. 根据数据内容人工判断列含义后重命名
EXEC sp_rename 'dbo.ObjectID_123456', 'hr.employees', 'OBJECT';
EXEC sp_rename 'hr.employees.col_1', 'emp_id', 'COLUMN';
EXEC sp_rename 'hr.employees.col_2', 'emp_name', 'COLUMN';
EXEC sp_rename 'hr.employees.col_3', 'department', 'COLUMN';
EXEC sp_rename 'hr.employees.col_4', 'hire_date', 'COLUMN';
EXEC sp_rename 'hr.employees.col_5', 'salary', 'COLUMN';
GO
-- 3. 修改列的数据类型(推断可能不准确,需要根据业务调整)
ALTER TABLE hr.employees ALTER COLUMN emp_id INT NOT NULL;
ALTER TABLE hr.employees ALTER COLUMN emp_name NVARCHAR(100);
ALTER TABLE hr.employees ALTER COLUMN hire_date DATETIME;
ALTER TABLE hr.employees ALTER COLUMN salary DECIMAL(18, 2);
GO
-- 4. 数据验证
SELECT TOP 10 * FROM hr.employees ORDER BY emp_id;
Q7:TRUNCATE 恢复几乎没有恢复出任何数据
原因分析:TRUNCATE 是三种恢复中难度最高、成功率最低的。TRUNCATE 执行后,SQLServer 立即将所有数据页标记为"空闲",这些空闲页随时可能被新的写操作覆盖。如果 TRUNCATE 后数据库继续运行了一段时间,即使只有少量写入,也可能导致大部分原始数据页被覆盖。
关键时间窗口:
- TRUNCATE 后立即停止所有写入(几分钟内):恢复成功率 50-70%
- TRUNCATE 后 1 小时内仍有写入:成功率可能降到 10% 以下
- TRUNCATE 后经过大量写入或收缩:成功率接近 0
建议的操作顺序:
# 按顺序尝试,每一步都单独保存输出
Step 1: ./fgsdu recover truncate mdf -schema XX -table YY -o step1_truncate.sql
Step 2: ./fgsdu recover all mdf -schema XX -table YY -o step2_all.sql
Step 3: ./fgsdu recover deep mdf -schema XX -table YY -o step3_deep.sql
将三步的结果汇总导入测试库,合并后去重。
Q8:导出的 SQL 文件中有大量带 "-- WARNING" 标记的行
原因说明:这是正常且预期的行为,不是程序故障。这些 WARNING 行是 5 重完整性校验发现的有问题的行,说明这些行在某些校验项上未通过。FGSDU 选择将这些行仍然导出(而不是丢弃),并在前面加上 WARNING 注释明确标记,让用户可以人工判断这些行是否可用。
典型的 WARNING 示例:
-- WARNING: Row 47 has integrity issues (NULL violations: 1, Length violations: 1)
INSERT INTO dbo.orders (order_id, customer_id, amount, remark)
VALUES (47, NULL, 100.50, 'This is a very long remark text that exceeds the max length limit of 50 characters...');
如何处理这些 WARNING 行:
-- 步骤 1:统计有问题的行数和类型
-- 直接在 SQL 文件中 grep 或导入后查询
SELECT
SUM(CASE WHEN customer_id IS NULL THEN 1 ELSE 0 END) AS NullCustomerID,
SUM(CASE WHEN LEN(remark) > 50 THEN 1 ELSE 0 END) AS LongRemark,
COUNT(*) AS TotalRows
FROM dbo.orders;
-- 步骤 2:根据业务规则处理
-- 示例 1:customer_id 为 NULL 的行可能是部分恢复的,与业务方确认后可能需要丢弃
DELETE FROM dbo.orders WHERE customer_id IS NULL;
-- 示例 2:remark 超长,截断后保留
UPDATE dbo.orders SET remark = LEFT(remark, 50) WHERE LEN(remark) > 50;
-- 示例 3:如果业务能容忍部分问题,可保留这些行不做处理
10.3 数据导入类问题
Q9:在 SSMS 中执行导出的 SQL 文件报错 "Insufficient memory" 或脚本执行超时
原因分析:导出的 SQL 文件过大(几百 MB 或几个 GB),SSMS 的编辑器无法一次性加载或执行超时。
解决方案:
# 方案 1(推荐):使用 sqlcmd 命令行执行,支持大文件
sqlcmd -S servername -E -d recovered_db -i huge_export.sql -b -o import.log
# 方案 2:使用 sqlcmd 设置更长的超时时间
sqlcmd -S servername -E -d recovered_db -i huge_export.sql \
-t 0 -b # -t 0 = 无查询超时限制
# 方案 3:导出时使用 DMP 格式,然后用 BCP 高速导入
# 步骤 A:导出为 DMP
./fgsdu export mydb.mdf orders -format dmp -o orders.dmp
# 步骤 B:在新库中建表(从 .recovery_steps.txt 中复制 CREATE TABLE)
# 步骤 C:用 BCP 原生导入
bcp recovered_db.dbo.orders in orders.dmp -S servername -T -n -b 10000
# 方案 4:将大 SQL 文件按行拆分后分批导入
# 使用 split 命令拆分(每个文件 10 万行)
split -l 100000 -d huge_export.sql part_
# 然后逐个执行拆分后的文件
Q10:导入后发现大量重复行,怎么办?
原因分析:重复行在以下恢复场景中是可能出现的:
- DELETE 恢复:同一行可能在多个扫描阶段都被找到(LSN、Ghost、页链)
- DROP/TRUNCATE 恢复:数据可能同时存在于正常页和孤立已释放页中
- 深度扫描:会尽可能多提取,可能造成重复
解决方案:执行去重 SQL(根据实际表的主键或唯一列调整):
-- 方法 A:有主键或唯一列时(推荐)
-- 假设主键是 order_id
;WITH CTE_Dedup AS (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY order_id -- 按主键去重
ORDER BY (SELECT 0) -- 保留第一条
) AS rn
FROM dbo.orders
)
DELETE FROM CTE_Dedup WHERE rn > 1;
GO
PRINT 'Deduplicated by primary key';
-- 方法 B:没有主键时,按所有列值去重
;WITH CTE_Dedup AS (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY col_1, col_2, col_3, col_4 -- 按所有列
ORDER BY (SELECT 0)
) AS rn
FROM dbo.inferred_table
)
DELETE FROM CTE_Dedup WHERE rn > 1;
GO
-- 方法 C:使用 SELECT DISTINCT INTO 重建表
-- 适用于没有主键且列数不多的情况
SELECT DISTINCT *
INTO dbo.orders_deduped
FROM dbo.orders;
GO
-- 验证行数差异
SELECT
(SELECT COUNT(*) FROM dbo.orders) AS OriginalRows,
(SELECT COUNT(*) FROM dbo.orders_deduped) AS DedupedRows;
GO
10.4 跨平台使用类问题
Q11:在 CentOS 5 上编译运行失败
解决方案:
# 推荐方法:在新系统上用 Docker 编译出静态二进制,复制到 CentOS 5 上
# 在新系统(如 Ubuntu 22.04)上执行:
cd FGSDU
make docker
# 将 build/fgsdu 复制到 CentOS 5,直接运行即可,无需任何依赖
# 验证二进制兼容性(在 CentOS 5 上执行)
file ./fgsdu
./fgsdu version
Q12:Windows 版本运行报错 "缺少 MSVCR*.dll" 或 "无法启动此程序,因为计算机中丢失 ..."
原因分析:使用了动态编译的 Windows 版本,但目标系统缺少对应版本的 Visual C++ 运行库。
解决方案:
- 使用
make win编译的版本是静态链接的,不依赖任何运行库,应直接使用该版本 - 如果是其他来源的动态编译版本,安装对应版本的 Visual C++ Redistributable Package,或改用静态版本
11. 附录
附录 A:命令速查表
# =====================================================
# 一、信息查询命令
# =====================================================
fgsdu help 显示完整帮助
fgsdu ? 帮助别名
fgsdu version / ver / -v 显示版本号
fgsdu scan / ls / list <mdf> 列出所有表
fgsdu info / fileinfo <mdf> 显示文件头信息
fgsdu query / qry <mdf> <table> 查询表结构
fgsdu stats / st <mdf> 显示数据库统计
fgsdu analyze / ana <mdf> 深度结构分析
fgsdu analyze <mdf> -log 2 调试级深度分析
# =====================================================
# 二、数据导出命令
# =====================================================
fgsdu export / exp / dump <mdf> <table> 导出表为SQL
fgsdu export <mdf> <table> -o <file> 指定输出路径
fgsdu export <mdf> <table> -format dmp 导出为DMP格式
fgsdu export <mdf> all 导出所有表
fgsdu unload / extract <mdf> -o <dir> 全库一键抽取
fgsdu unload <mdf> -user <name> 按用户过滤抽取
fgsdu unload <mdf> -schema <name> 按Schema过滤抽取
fgsdu unload <mdf> -format dmp 抽取为DMP格式
# =====================================================
# 三、数据恢复命令
# =====================================================
# DELETE 恢复
fgsdu recover / rec / restore delete <mdf> 恢复所有DELETE数据
fgsdu rec delete <mdf> -table <name> 按表名恢复
fgsdu rec delete <mdf> -schema <name> 按Schema恢复
fgsdu rec delete <mdf> -user <name> 按用户恢复
# DROP 恢复
fgsdu rec drop <mdf> 恢复所有DROP表
fgsdu rec drop <mdf> -schema <name> 按Schema恢复
fgsdu rec drop <mdf> -user <name> 按用户恢复
# TRUNCATE 恢复
fgsdu rec truncate <mdf> 恢复TRUNCATE数据
fgsdu rec truncate <mdf> -table <name> 按表名恢复
fgsdu rec truncate <mdf> -schema <name> 按Schema恢复
# 全量恢复(DELETE+DROP+TRUNCATE)
fgsdu rec all <mdf>
fgsdu rec all <mdf> -schema <name> -user <name> 组合过滤
# 深度扫描(最大恢复)
fgsdu rec deep <mdf>
fgsdu rec deep <mdf> -schema XX -table YY -o out.sql
# =====================================================
# 四、多文件数据库(所有命令通用)
# =====================================================
fgsdu <任意命令> primary.mdf -ndf file1.ndf -ndf file2.ndf ...
# =====================================================
# 五、全局选项
# =====================================================
-o <path> 指定输出文件或目录
-format sql|dmp 输出格式(默认sql)
-log 0|1|2 日志级别(0静默/1信息/2调试)
-h / --help 显示帮助
附录 B:页类型与数据类型速查
SQLServer 页类型:
| 类型值 | 类型名称 | 说明 |
|---|---|---|
| 1 | DATA_PAGE | 堆或聚集索引的叶子节点数据页 |
| 2 | INDEX_PAGE | 聚集索引的非叶子节点或非聚集索引页 |
| 3 | TEXT_MIX_PAGE | 小型 LOB 数据混合页(text/ntext/image/varchar(max)) |
| 4 | SORT_PAGE | 排序操作临时页 |
| 7 | GAM_PAGE | 全局分配映射页(每 4GB 一个) |
| 8 | SGAM_PAGE | 共享全局分配映射页(每 4GB 一个) |
| 9 | IAM_PAGE | 索引分配映射页(跟踪对象占用的区) |
| 10 | PFS_PAGE | 页可用空间页(每 8088 页一个,记录每页空间使用率) |
| 11 | BULK_CHANGED_MAP | 大容量操作变更映射页 |
| 13 | DIFF_CHANGED_MAP | 差异备份变更映射页 |
| 14 | SYSTEM_PAGE | 系统页 |
常用 SQLServer 数据类型编码:
| 类型编码 | 数据类型 | 固定长度 | 说明 |
|---|---|---|---|
| 48 | TINYINT | 1 字节 | 0 到 255 的无符号整数 |
| 52 | SMALLINT | 2 字节 | -32768 到 32767 |
| 56 | INT | 4 字节 | -2^31 到 2^31-1 |
| 127 | BIGINT | 8 字节 | -2^63 到 2^63-1 |
| 59 | REAL | 4 字节 | 单精度浮点数 |
| 62 | FLOAT | 8 字节 | 双精度浮点数 |
| 108 / 122 | NUMERIC / DECIMAL | 可变 | 固定精度和小数位数的数值 |
| 60 | MONEY | 8 字节 | -922,337,203,685,477.5808 到 +922,337,203,685,477.5807 |
| 122 | SMALLMONEY | 4 字节 | -214,748.3648 到 +214,748.3647 |
| 40 | DATE | 3 字节 | 0001-01-01 到 9999-12-31 |
| 41 | TIME | 可变 | 00:00:00.0000000 到 23:59:59.9999999 |
| 61 | DATETIME | 8 字节 | 1753-01-01 到 9999-12-31,精度 3.33ms |
| 42 | DATETIME2 | 可变 | 0001-01-01 到 9999-12-31,精度 100ns |
| 43 | DATETIMEOFFSET | 可变 | 带时区偏移的日期时间 |
| 175 | CHAR | 固定 | 定长非 Unicode 字符串,最多 8000 字符 |
| 167 | VARCHAR | 可变 | 变长非 Unicode 字符串,最多 8000 字符(MAX 2GB) |
| 239 | NCHAR | 固定 | 定长 Unicode 字符串,最多 4000 字符 |
| 231 | NVARCHAR | 可变 | 变长 Unicode 字符串,最多 4000 字符(MAX 2GB) |
| 35 | TEXT | 可变 | 旧版变长非 Unicode 大文本,最多 2GB |
| 99 | NTEXT | 可变 | 旧版变长 Unicode 大文本,最多 2GB |
| 34 | IMAGE | 可变 | 旧版变长二进制大数据,最多 2GB |
| 173 | BINARY | 固定 | 定长二进制数据,最多 8000 字节 |
| 165 | VARBINARY | 可变 | 变长二进制数据,最多 8000 字节(MAX 2GB) |
| 36 | UNIQUEIDENTIFIER | 16 字节 | GUID(全局唯一标识符) |
| 104 | BIT | 1 字节(实际按位存储) | 布尔值 0 或 1 |
附录 C:数据恢复成功率参考总表
| 操作类型 | 几分钟内立即恢复 | 1 小时内 | 1 天内 | 数天后 | DBCC SHRINKFILE 后 |
|---|---|---|---|---|---|
| DELETE 误删除 | 90%-95% | 70%-90% | 30%-70% | 10%-30% | 低于 10% |
| DROP TABLE 误删表 | 60%-80% | 40%-60% | 20%-40% | 低于 10% | 低于 5% |
| TRUNCATE TABLE 清空 | 50%-70% | 30%-50% | 10%-30% | 低于 5% | 低于 5% |
| (追加)深度扫描提升 | +10%-20% | +10%-20% | +5%-10% | +5% | 基本无效 |
重要说明:以上数据仅为基于大量实践案例的经验参考值,实际恢复成功率受到无数具体因素的综合影响,包括但不限于:数据库大小、删除后数据写入量、SQLServer 版本、数据库恢复模式、Ghost 清理线程调度频率、是否有索引重建操作、磁盘类型等。切勿将上表数据作为恢复效果的保证。
作者: 风哥
WX:itpux-com
官方网站 : http://www.fgedu.net.cn , http://www.itpux.com
数据库教程 : https://edu.51cto.com/lecturer/8020378.html
提高恢复成功率的黄金法则:
- 发现数据丢失后立即停止所有数据库写入操作,这是最重要的一条
- 不要执行 DBCC SHRINKFILE、数据库收缩、索引重建、大量批处理写入等操作
- 立即对 MDF/NDF 文件做完整备份副本,所有恢复操作都在副本上进行
- 尽快执行恢复操作,时间每过去一分钟,恢复成功率就可能下降一些
- 优先使用带精确过滤条件的恢复(
-schema+-table),再逐级尝试更大范围的恢复

浙公网安备 33010602011771号