注册
达梦 DM8 灾难恢复实战:控制文件丢失 + 回滚段损坏 + REDO 丢失,dmdul 离线抢救全记录
培训园地/ 文章详情 /

达梦 DM8 灾难恢复实战:控制文件丢失 + 回滚段损坏 + REDO 丢失,dmdul 离线抢救全记录

hdfeng 2026/09/09 26 1 0

摘要:本文记录一次达梦 DM8 的极端灾难场景演练——控制文件被删、回滚段头部损坏、REDO 日志丢失,实例彻底无法启动。在常规恢复手段失效的情况下,使用 dmdul 工具仅凭 SYSTEM.DBF + MAIN.DBF 两个数据文件,完整恢复了 10 张表共 1000 行数据和 10 个对象(序列、函数、存储过程、触发器、视图、同义词),并通过序列防重号、数据精度、大对象等抽查验证了恢复质量。文末附踩坑总结,希望对遇到类似灾难场景的同学有所帮助。


一、写在前面

做 DBA 的都知道,数据库最怕的不是慢,而是起不来。控制文件丢失、REDO 损坏这类故障一旦发生,常规的备份还原流程往往无从下手——连实例都不认识了,谈何恢复?

本次实验的目的,就是构造这样一个"最坏情况",并验证 dmdul 离线恢复工具在极端场景下的能力。整个实验按四个条件闭环推进:

  1. 构造测试数据——类型多样、可精确验证;
  2. 备份——冷备全量文件并记录校验和,留好"后悔药";
  3. 破坏实例——使其彻底无法启动;
  4. dmdul 离线恢复 + 还原实例 + 逐项验证

二、实验环境

项目 说明
实验日期 2026-09
实验服务器 192.168.1.xxx(内网,RHEL 7.6,x86_64)
数据库版本 DM8(dmrman V8,备份集 V8.10.18.2),实例 DMSERVER
dmdul 版本 v0.9.0(2026-08-22 构建)
DM 安装目录 /data/dmdbms
数据目录 /dmdata/DAMENG(SYSTEM.DBF 256MB / MAIN.DBF 128MB / ROLL.DBF 128MB / TEMP.DBF 128MB)
服务 DmServiceDM.service(systemd 托管)
归档 LOCAL 归档至 /dmdata/dmarch
连接方式 SYSDBA@5236(密码已脱敏,通过 JDBC 连接)
dmdul 工作区 /data/dmdul_work/snap(离线 DBF 快照副本)
冷备份目录 /dmdata/dmbak/DUL_TEST_COLD_20260827
恢复输出目录 /dmdata/dmbak/DUL_RECOVER_20260827

三、第一步:构造"可验证"的测试数据

恢复工具好不好,得有精确的"标尺"来量。我在 TESTDUL 用户下执行了 36 条 SQL,构造了10 张表 + 10 个其它对象,全部执行成功。

3.1 十张测试表(每张 100 行,类型多样化)

关键列 覆盖点
T_TEST01 INT PK, VARCHAR, DECIMAL(12,2), DATE, TIMESTAMP 常规组合
T_TEST02 BIGINT PK, VARCHAR, NUMERIC(18,4), CHAR DEFAULT 大整数 + 数值精度
T_TEST03 SMALLINT PK, CHAR(20), FLOAT 短整数 + 浮点
T_TEST04 INT PK, VARCHAR, BLOB, CLOB 大对象(行内小值)
T_TEST05 INT PK, DATE, TIME, INT 时间类型
T_TEST06 INT PK, VARCHAR, TIMESTAMP 日志表
T_TEST07 INT PK, VARCHAR, INT 触发器目标表
T_TEST08 INT PK, DECIMAL(20,6), INT, VARCHAR 高精度数值
T_TEST09 INT PK, FK_ID, VARCHAR + 外键 FK_T09→T_TEST01 外键约束
T_TEST10 INT PK, VARCHAR(可空), INT NULL + 边界值(±2^31)

3.2 十个其它对象

# 对象 说明
1 SEQ_TEST01 正向序列 NOCACHE,已取值(nextval=3)
2 SEQ_TEST02 START 1000 INCREMENT 2 CACHE 20
3 SEQ_TEST03 反向序列 INCREMENT -1 MIN 1 MAX 100
4 FUNC_TEST01 函数(A+B)
5 FUNC_TEST02 函数(字符串拼接)
6 PROC_TEST01 存储过程(插入日志)
7 PROC_TEST02 存储过程(OUT 参数统计)
8 TRG_TEST01 触发器(BEFORE INSERT,引用 :NEW 与序列)
9 V_TEST01 视图(WHERE AMOUNT > 5000,51 行)
10 SYN_TEST01 同义词(指向 T_TEST02)

另外附带 10 个主键索引、10 个主键约束和 1 个外键 FK_T09。

为什么要这么设计? 序列取过值(nextval=3)是为了验证恢复后防重号;NULL 和 ±2^31 边界值考验数据还原精度;BLOB/CLOB 考验大对象;外键、触发器、同义词则考验 DDL 还原的完整度。后面每一项都逐一验证了。


四、第二步:冷备份,然后"炸掉"实例

4.1 停库冷备

systemctl stop DmServiceDM # 全量冷拷贝至备份目录(DBF×4 + dm.ctl + dm.ini + dmarch.ini # + REDO×2 + sqllog.ini + dm_service.prikey + ctl_bak) cp -a /dmdata/DAMENG/* /dmdata/dmbak/DUL_TEST_COLD_20260827/ # 记录 sha256 校验和,供还原后比对 sha256sum /dmdata/dmbak/DUL_TEST_COLD_20260827/* > sha256.txt

4.2 三连破坏(实例已停)

# 1. 删除控制文件 rm -f /dmdata/DAMENG/dm.ctl # 2. 损坏回滚段头部 64KB dd if=/dev/urandom of=/dmdata/DAMENG/ROLL.DBF bs=1024 count=64 conv=notrunc # 3. 删除 REDO 日志 rm -f /dmdata/DAMENG/DAMENG01.log /dmdata/DAMENG/DAMENG02.log # SYSTEM.DBF / MAIN.DBF / TEMP.DBF 保持完好(供 dmdul 读取)

4.3 验证实例确实起不来了

$ systemctl start DmServiceDM Job for DmServiceDM.service failed ... Active: failed (Result: exit-code) $ su - dmdba -c "timeout 10 /data/dmdbms/bin/dmserver path=/dmdata/DAMENG/dm.ini" file dm.key not found, use default license! Read ini error, name:CTL_PATH, value:/dmdata/DAMENG/dm.ctl nsvr_ini_file_read failed, [code: -803]

实例因控制文件丢失无法启动,常规恢复流程失效——这正是本次实验要面对的场景。


五、第三步:dmdul 离线恢复(核心环节)

5.1 准备只读快照

dmdul 的红线之一是绝不在原始文件上操作,先复制一份:

mkdir -p /data/dmdul_work/snap cp /dmdata/DAMENG/SYSTEM.DBF /dmdata/DAMENG/MAIN.DBF /data/dmdul_work/snap/

5.2 bootstrap:重建字典

DMDUL> set data_dir /data/dmdul_work/snap; DMDUL> set system /data/dmdul_work/snap/SYSTEM.DBF; DMDUL> set output_dir /dmdata/dmbak/DUL_RECOVER_20260827; DMDUL> set data_format sql; DMDUL> bootstrap;

关键输出(全部 status=OK):

[bootstrap] phase=metadata status=OK db_name="DAMENG" instance_name="DMSERVER" page_size=32768 charset="GB18030" case_sensitive=1 [bootstrap] phase=precheck name=SYSTEM.DBF status=OK bad=0 checksum=0 ← 物理预检 [bootstrap] stage=1 phase=anchor name=SYSOBJECTS mode=root-chain status=OK root=0/16 rows=1253 [bootstrap] stage=1 phase=anchor name=SYSINDEXES mode=root-chain status=OK root=0/256 rows=179 [bootstrap] stage=2 phase=dictionary name=SYSCOLUMNS mode=root-chain status=OK rows=4887 [bootstrap] stage=2 phase=extract name=SEQUENCE_STATE mode=runtime-page-slot status=OK pages=1 rows=3 ← 序列运行页直读 [bootstrap] phase=complete status=SUCCESS mode=standard-two-stage objects=1253 elapsed_ms=16411

字典重建结果:users=2, schemas=3, tables=12, columns=86, views=1, sequences=3, routines=5, triggers=1, synonyms=9。

再用 list user 盘点确认 TESTDUL 用户下的对象一个不少:10 表 + 1 视图 + 1 同义词 + 3 序列 + 1 触发器 + 2 函数 + 2 过程。

值得注意SEQUENCE_STATE 是从序列运行页直接读出来的(runtime-page-slot),这一步是后面"防重号"验证的基础。

5.3 unload:导出 DDL 与数据

DMDUL> unload object TESTDUL; -- 10 个对象 DDL DMDUL> unload user TESTDUL; -- 10 张表 DDL + 数据

导出统计:

objects: tables=10 views=1 sequences=3 routines=4 triggers=1 synonyms=1 unload user: rows exported: 1000 rows failed: 0 planned pages: 10 direct pages read: 10 fallback pages scanned: 0 fallback reason: none ← page plan 全直读,零回退

1000 行全部导出、0 行失败、页面计划全直读零回退,干净利落。

5.4 恢复产物清单

输出目录 /dmdata/dmbak/DUL_RECOVER_20260827/ 共 21 个文件:

文件 内容
TESTDUL_objects.sql 用户定义 + 10 表 DDL + 序列×3 + 函数×2 + 过程×2 + 触发器 + 视图 + 同义词 + PK×10 + 外键 FK_T09
TESTDUL_T_TEST01~10_ddl.sql 各表建表 DDL(含 STORAGE CLUSTERBTR)
TESTDUL_T_TEST01~10_data.sql 各表 100 行 INSERT 数据(共 1000 行)

5.5 恢复内容质量抽查

检查项 结果
10 表各 100 行数据 ✅ rows exported: 1000, rows failed: 0
数据精度 ✅ NAME_001、100.5、DATE '2026-01-02'、TIMESTAMP '2026-01-01 08:01:00.000000' 精确
NULL 与边界值 ✅ T_TEST10 第 100 行 NULL, 2147483647
BLOB / CLOB ✅ HEXTORAW('00000001')、CLOB 内容 RPAD 完整
主键/外键 ✅ 10 个 ALTER TABLE ... ADD PRIMARY KEY + FK_T09
序列(防重号) ✅ SEQ_TEST01 START WITH 4(运行页恢复 LAST_NUMBER,而非初始值 1)
反向序列 ✅ SEQ_TEST03 INCREMENT BY -1 MINVALUE 1 MAXVALUE 100
触发器 ⚠️ 源码中 :NEW.GEN_VAL 被解码为 :E4rIGEN_VAL(GB18030 源码解码瑕疵),需人工修正后再回灌
用户密码 ⚠️ 无法还原原密码(达梦加密存储),dmdul 生成新密码(已脱敏)并写注释说明

两个 ⚠️ 说明 dmdul 的定位很清晰:导出的 DDL 必须人工审查后,才能在隔离测试库回灌


六、第四步:实例还原与验证闭环

dmdul 抢救出数据后,再把实例本身从冷备份完整还原:

# 1. 从冷备份还原全部文件 cp -a /dmdata/dmbak/DUL_TEST_COLD_20260827/{SYSTEM,MAIN,ROLL,TEMP}.DBF /dmdata/DAMENG/ cp -a /dmdata/dmbak/DUL_TEST_COLD_20260827/{dm.ctl,dm.ini,dmarch.ini,sqllog.ini,dm_service.prikey,DAMENG01.log,DAMENG02.log} /dmdata/DAMENG/ # 2. 属主修复(关键!root 拷贝会破坏属主,导致 dmap 写入失败) chown -R dmdba:dinstall /dmdata/DAMENG/* # 3. sha256 校验和比对:7/7 OK # 4. 启动 systemctl start DmServiceDM → active

恢复后通过 JDBC 验证:

  • 10 张表全部存在,行数 100 / 100 / … / 100;
  • 10 个对象全部存在:SEQ_TEST01/02/03、FUNC_TEST01/02、PROC_TEST01/02、TRG_TEST01、V_TEST01、SYN_TEST01;
  • SEQ_TEST01.nextval = 4,与 dmdul 恢复的 START WITH 4 一致,防重号闭环验证通过
  • FUNC_TEST01(20,22) = 42,函数可用;
  • V_TEST01 行数 51,视图数据正确;SYN_TEST01 行数 100,同义词可用。

至此,四个条件全部闭环:数据救回来了,实例也还原了,且数据无损。


七、踩坑总结(干货)

  1. disql 无法连接含 # 的密码# 是 disql 登录选项(#{})的语法前缀,形如 用户名/含#密码@host 的连接串会报"用法:CONN[ECT]"或登录失败(dexp 同样报"登录失败")。解决方案:改用 JDBC(jdbc:dm://host:port + 用户/密码参数)连接,或者干脆让密码避开 #
  2. dmdul 三条红线:不能在实例运行时读取数据文件(必须停库或用一致性快照);不能在原始文件上操作(务必复制到独立恢复目录);导出结果必须先在隔离测试库验证。
  3. 文件属主要修好:还原/恢复后的文件必须 chown dmdba:dinstall。用 root 直接拷贝会破坏属主,导致 dmap 写入失败、实例起不来——这是高频坑。
  4. 触发器源码解码瑕疵:GB18030 库中,:NEW 可能被解码成 :E4r。回灌前需人工检查 SYSTEXTS 类源码对象。
  5. 密码机制:达梦密码是加密存储的,离线恢复只能重置密码,无法还原原文。
  6. 序列防重号:dmdul 通过 SYSOBJECTS.INFO5 定位器直读序列运行页,恢复安全的 LAST_NUMBER(实测 nextval=4 与恢复值一致)。
  7. 无需 dm.ctl:dmdul 在控制文件丢失时照常工作(dm.ctl 只是可选增强),这正是它能兜底极端灾难的原因。

八、总结

  • dmdul v0.9.0 在"控制文件丢失 + 回滚段损坏 + REDO 丢失"的极端场景下,仅凭 SYSTEM.DBF + MAIN.DBF 即完成 10 张表 1000 行数据 + 10 个对象的完整离线恢复,page plan 全直读、零回退;
  • 恢复出的 DDL/数据质量高:类型、精度、NULL、BLOB/CLOB、约束、序列防重号均正确;但触发器源码存在 GB18030 解码瑕疵、用户密码需重置——导出的 DDL 必须人工审查后才能在隔离测试库回灌
  • 实例通过冷备份完整还原,数据无损。

一句话:备份是底线,dmdul 是底线之上的"最后一根稻草"。再完善的备份体系,也建议提前了解这类离线抢救工具的边界和用法——真出事的时候,它就是救命的那一手。


附注

  1. 本文由内部实验报告整理而成,服务器 IP、账号密码等敏感信息均已脱敏。
  2. 实验环境已在服务器上保留完整痕迹(冷备份、恢复输出、dmdul 字典与日志),可复现复盘。

原创内容,转载请注明出处。欢迎在评论区交流达梦灾恢实践~

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服