跳过正文

DMDUL:达梦数据库离线恢复与数据抽取工具

·789 字·4 分钟
Greatfinish
作者
Greatfinish
记录 Oracle、PostgreSQL、达梦、Linux、存储与生产环境故障处理经验。

dmdul

项目地址: GitHub
版本下载: GitHub Releases

项目简介
#

DMDUL 是一个使用 Go 编写的达梦数据库离线恢复与数据抽取工具

当数据库无法正常 open、控制文件或日志链路异常、常规恢复手段无法继续,但仍能取得 SYSTEM.DBF、可选的 dm.ctl 以及用户表空间 DBF 文件时,DMDUL 可以直接从离线物理文件中:

  • 识别数据库页大小、簇大小、字符集、大小写敏感标志等初始化参数;
  • 恢复用户、表、字段、索引、约束、视图、序列、过程、函数、包、触发器、同义词和授权;
  • 将表数据导出为 SQL、CSV 或达梦原生兼容的纯数据 DMP;
  • 尝试恢复 DELETEDROPTRUNCATE 后尚未被覆盖的物理残留数据;
  • 处理分区表、行外 LOB、Long Row、大表和超过 4 GiB 的输出场景。

DMDUL 不是 DMRMAN、归档恢复、闪回、dexp 的替代品。它更适合数据库已经无法通过正常路径恢复时,作为最后一道离线救援手段。所有导出结果都应先在隔离测试库验证。

为什么要做 DMDUL
#

达梦数据库发生严重故障后,常规处理通常优先依赖备份、归档、控制文件和数据库自身恢复机制。但在以下场景中,DBA 可能只剩下一组 DBF 文件:

  1. 实例无法正常启动或无法打开;
  2. 控制文件、ROLL、REDO 或归档链路不完整;
  3. 误执行 DROPTRUNCATE 后需要验证物理页是否仍有残留;
  4. 需要研究 SYSTEM 表空间、系统字典和数据页的真实组织方式;
  5. 需要从损坏库中尽可能多地恢复 DDL 和业务数据。

DMDUL 的核心思路不是“猜测 SQL”,而是建立一条从原始物理地址到逻辑对象的证据链:

SYSTEM.DBF / dm.ctl / user tablespace DBF
                    |
                    v
            Standard Bootstrap
                    |
                    v
           Dictionary Recovery
                    |
          +---------+---------+---------+
          |         |         |         |
          v         v         v         v
         DDL       SQL       CSV       DMP
                                        |
                                        v
                                      dimp
                                        |
                                        v
                                Dameng Database

对 SYSTEM.DBF 的结构理解
#

下图概括了 DMDUL 当前对 SYSTEM.DBF 的结构理解:从文件头和初始化参数区出发,定位 SYSOBJECTSSYSCOLUMNSSYSCOLINFOS 等核心字典,再逐步还原用户、表、列和类型信息。

SYSTEM.DBF 结构与数据字典定位示意图

Standard Bootstrap
#

DMDUL 的 bootstrap; 不只是全文件关键字扫描,而是优先采用两阶段标准字典下载流程:

第一阶段:定位核心字典入口

  • SYSTEM.DBF page 0 的 anchor 定位 SYSOBJECTS
  • 定位 SYSINDEXES
  • 读取 storage root;
  • 解析 root/internal/leaf 页链;
  • 校验页地址、页类型和 storage identity。

第二阶段:下载扩展字典

根据第一阶段恢复的对象信息,继续定位并解析:

  • SYSCOLUMNS
  • SYSTEXTS
  • SYSGRANTS
  • 分区相关系统字典;
  • 视图、过程、函数、包、触发器和授权源码。

当 anchor、root page 或 leaf chain 损坏时,DMDUL 才回退到按页流式扫描,并在 dul.log 中记录 fallback 原因。

数据页与行记录
#

下面是基于 DMDUL 研究和实验整理的典型 DM8 8K 数据页示意图。

DM8 8K 数据页结构示意图

典型数据页可概括为:

  • 低地址区域保存页头和行记录;
  • 行记录从低地址向高地址增长;
  • slot directory 通常位于高地址区域并向低地址增长;
  • slot 中保存行记录偏移;
  • 页尾还可能保存校验摘要、标志和其他尾部元数据。

行头语义
#

在真实样例中,行记录开头的两个字节应按大端 u16 解释:

低 15 位:物理行长度
0x8000 :DELETE 标志

因此:

00 2E 00 ...

表示物理行长为 0x002E = 46 字节,第三字节才进入列 metadata。它不能被解释为“0x2E 代表删除行”。

普通 unload 只解析 slot directory 中可寻址的记录;删除 slot、无 slot 物理空洞以及 DROP/TRUNCATE 后的残留页,只由 recover table 恢复模式扫描。

PAGE_CHECK
#

DMDUL 当前支持识别 DM8 常见 PAGE_CHECK 模式:

模式处理方式
0无页校验
1页头 0x18 的 CRC32/IEEE
2页尾 HASH,slot 目录需按摘要长度前移
3页头 0x18 的 CRC32C/Castagnoli

这使 SYSTEM 字典页、分区页和普通数据页在不同 PAGE_CHECK 配置下仍能正确定位 slot directory。

上述结构来自 DMDUL 项目研究、差分实验和真实样例观察,不是达梦官方文件格式规范。不同 DM8 版本、表类型和存储策略可能存在差异。

表数据定位:不再默认扫描整个数据文件
#

DMDUL 当前正常表数据导出采用 page plan 直读:

storage_id / root_file / root_page
                |
                v
             root page
                |
          +-----+-----+
          |           |
        0x14         0x15
      leaf/data    root/internal
          |           |
          +-----+-----+
                |
                v
          leaf next chain
                |
                v
         ReadAt planned pages

执行顺序如下:

  1. 从磁盘字典取得表或叶子分区的 storage_idroot_fileroot_page
  2. 读取 root page;
  3. root 为 0x14 时按 leaf/data page 处理;
  4. root 为 0x15 时解析 BTree root/internal child refs;
  5. 沿 leaf 页 next 链生成 page plan;
  6. 计划成功后,仅使用 ReadAt 读取计划页;
  7. 每个计划页再次校验 group/file/page identity、page kind 和 storage id。

仅在主路径失败时,才依次执行:

同 group 文件按 storage_id 扫描
        -> segment range fallback
        -> recover table 全文件残留页扫描

因此,普通 unload tableunload userunload database 不再默认全面扫描整个数据文件。控制台和 dul.log 会记录:

planned pages: 12
direct pages read: 12
fallback pages scanned: 0
fallback reason: none

支持恢复的对象
#

DMDUL 当前可以离线恢复或生成以下对象 DDL:

  • 用户和角色授权;
  • 表、字段、默认值、注释;
  • 索引、主键、唯一约束、外键、CHECK 约束;
  • RANGE、LIST、HASH 分区表;
  • 视图;
  • 序列;
  • 存储过程、函数、包和包体;
  • 触发器;
  • 同义词;
  • 表、视图和序列等对象授权。

bootstrap; 会将结果持久化到 dmdul_dict/

dmdul_dict/
├── meta.tsv
├── users.tsv
├── tables.tsv
├── columns.tsv
├── views.tsv
├── sequences.tsv
├── routines.tsv
├── triggers.tsv
├── synonyms.tsv
└── tab_privs.tsv

这些 TSV 文件可以人工审查和修订。再次启动后执行:

DMDUL> load dictionary;

即可直接使用磁盘字典,不必重复扫描 SYSTEM.DBF

SQL、CSV 与 DMP 三种数据格式
#

DMDUL 支持三种数据导出格式:

DMDUL> set data_format sql;
DMDUL> set data_format csv;
DMDUL> set data_format dmp;

其中 SQL 为默认格式。

SQL
#

生成可人工审查的 INSERT INTO 语句,适合小规模恢复和复杂类型核查。

DMDUL> unload table HR_TEST.EMP_INFO;

CSV
#

每张非空表生成一个 CSV 文件,适合数据分析、人工检查和中间迁移。

DMDUL> set data_format csv;
DMDUL> unload user HR_TEST;

达梦 DMP
#

DMDUL 可以生成可由达梦官方 dimp 装载的纯数据 DMP:

DMDUL> set data_format dmp;
DMDUL> unload table HR_TEST.EMP_INFO;
DMDUL> unload user HR_TEST;
DMDUL> unload database;

主要特性包括:

  • 表级、用户级和整库级导出;
  • 每张表一个 DMP,便于逐表重试和并行导入;
  • UTF-8、GB18030、EUC-KR 文件头;
  • RANGE、LIST、HASH 分区表;
  • 行外 CLOB/BLOB locator 页链流式读取;
  • STORAGE(USING LONG ROW)
  • 64 位长度和多 phase 输出;
  • 整表或整份 DMP 超过 4 GiB;
  • 自动写入 page size、extent size、charset 和 CASE_SENSITIVE

示例导入:

dimp SYSDBA/password \
  FILE=HR_TEST_EMP_INFO_data.dmp \
  DATA_ONLY=Y \
  FAST_LOAD=Y

需要注意:

  • JSON/JSONB 表应使用 FAST_LOAD=N
  • 当前已验证的 DMP 路径不能无损保存 TIME 小数秒,要求无损时应使用 SQL 或 CSV;
  • DMP 是纯数据文件,对象、用户和授权仍由 DDL SQL 恢复。

数据类型覆盖
#

DMDUL 已覆盖常见 DM8 类型的 DDL、SQL、CSV 和 DMP 路径,包括:

  • BIT、BYTE、BINARY、RAW、PLS_INTEGER;
  • INT、BIGINT、NUMBER、DECIMAL;
  • REAL、FLOAT、DOUBLE;
  • CHAR、VARCHAR、VARCHAR2、国家字符类型;
  • DATE、TIME、DATETIME、TIMESTAMP;
  • 带时区时间类型;
  • 13 种 INTERVAL;
  • ROWID、BFILE;
  • JSON、JSONB;
  • CLOB、BLOB、TEXT、IMAGE;
  • 行内 LOB、行外 LOB 和 Long Row。

快速开始
#

准备文件
#

建议将文件放在同一工作目录:

D:\temp\oldpro\
├── SYSTEM.DBF
├── dm.ctl
├── MAIN.DBF
├── ROLL.DBF
├── TEMP.DBF
└── TBS_*.DBF

启动交互模式
#

Windows:

.\dmdul.exe

Linux:

./dmdul

标准流程
#

DMDUL> set system D:\temp\oldpro\SYSTEM.DBF;
DMDUL> set data_dir D:\temp\oldpro;
DMDUL> bootstrap;
DMDUL> show parameter;
DMDUL> list user;
DMDUL> list table HR_TEST;
DMDUL> unload object HR_TEST;
DMDUL> set data_format dmp;
DMDUL> unload user HR_TEST;
DMDUL> exit;

执行后目录结构类似:

D:\temp\oldpro\
├── control.dul
├── init.dul
├── dul.log
├── dmdul_dict\
└── output\
    ├── HR_TEST_ddl.sql
    ├── HR_TEST_EMP_INFO_data.dmp
    └── ...

DROP、TRUNCATE 与 DELETE 残留恢复
#

表被 TRUNCATEDROP 后,只要原数据页尚未被重新覆盖,就可能仍保留物理内容:

DMDUL> recover table USERS1.T_TEST;

如果表已被 DROP,当前 SYSTEM 字典中可能已经不存在表定义,需要:

  1. 加载 DROP 前保存的 dmdul_dict
  2. 或人工补齐 tables.tsvcolumns.tsv
  3. 必要时补充 storage_idroot_fileroot_pageassist_ids

普通 unloadrecover table 的语义不同:

  • 普通 unload:slot-only,面向当前可寻址物理记录;
  • recover table:允许读取删除 slot、无 slot 空洞和残留页。

但 slot-only 仍不等同于 committed-only。未提交 INSERT/DELETE 的最终可见性,仍需要后续离线事务状态和完整 Undo PRE IMAGE 链才能准确判断。

常用命令
#

bootstrap;
load parameter;
load dictionary;
show parameter;
list user;
list table <owner>;
unload object <owner|all>;
unload table <owner.table_name>;
unload user <owner>;
unload database;
recover table <owner.table_name>;
set data_format sql;
set data_format csv;
set data_format dmp;
set output_dir <directory>;
exit;

DMDUL 已移除原来的功能性命令行子命令。现在只保留:

dmdul
dmdul help
dmdul version

所有恢复和卸载操作统一在交互式 Shell 中完成。

当前边界
#

DMDUL 仍处于持续研究和验证阶段,当前边界包括:

  • 工具只读 DBF,不会修改原始数据文件;
  • 导出的 DDL、SQL、CSV 和 DMP 必须先在隔离测试库验证;
  • DROP/TRUNCATE 恢复依赖原数据页是否已经被覆盖;
  • DMP 当前是纯数据文件,不包含完整 dexp 元数据;
  • 行外 LOB、Long Row、损坏页和断链场景仍需要更多样例;
  • 迁移行、链式行尚未进行未经验证的跨页拼接;
  • 普通 slot-only 解析不等于事务一致性恢复;
  • 不保证恢复结果与故障前数据库在事务层面完全一致。

下载与参与
#

Windows 和 Linux x64 版本可从 GitHub Releases 下载:

欢迎提交 Issue、失败样例、测试数据和 Pull Request。对于离线恢复项目而言,真实失败案例和可重复实验样例比单纯增加代码更有价值。


本文中的 SYSTEM.DBF 和数据页示意图均基于 DMDUL 项目的研究与实验观察,仅用于解释解析思路,不代表达梦官方文件格式规范。