2026-10-26  ·  故障恢复

达梦利用备份恢复误删表(时间点恢复)

故障恢复

📋 想照着做?这篇有 交互操作版——左侧步骤卡片导航、命令一键复制,现场照着敲更方便:打开交互操作版 →

本文内容与交互版逐条对应,命令均为实测输出,未做删改。占位符 <…> 请按本机环境替换后执行。

先看结论:你手里有什么,决定能救回多少

误删表能不能救回来,只取决于两样东西:全库备份和归档日志。备份决定"能回到哪个基线",归档决定"能从基线往前走多远"。两者齐备,误删的数据能完整找回;缺归档,就只能回到备份那一刻,备份之后写入的数据一律丢失。

九步速成:把误删的表连数据一起救回来

第 1 步:确认表真没了,记下误删前时间

-- 1) 确认表确实不存在了(返回 0 就是没了)
SELECT COUNT(*) AS EXISTS_CNT
  FROM DBA_TABLES
 WHERE OWNER = '<模式名>'
   AND TABLE_NAME = '<表名>';

-- 2) 记下恢复目标时间点(误删前一刻)
SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS') AS T1 FROM DUAL;

实测输出:EXISTS_CNT = 0,时间点 2026-10-26 22:19:56。返回 0 说明表已经不在数据字典里,任何"查表"手段都看不到它。接下来能救它的只有备份。

时间点取在哪:要取误删动作之前、最后一次业务写入之后。本次实测在插入 50 行后等了 10 秒才记时间、又等 10 秒才 DROP,就是为了留出足够宽的窗口 —— 只留 3 秒时,秒级精度很容易踩到 DROP 那一秒上,恢复出来表还是空的。

第 2 步:核查全备与归档是否具备

-- 归档模式:Y=已开,N=未开
SELECT ARCH_MODE, STATUS$ FROM V$DATABASE;

-- 归档配置开关(1=启用 dmarch.ini)
-- 查看 dm.ini 与 dmarch.ini
grep -E "^\s*ARCH_INI" $DM_HOME/../dmdata/DAMENG/dm.ini
cat $DM_HOME/../dmdata/DAMENG/dmarch.ini

-- 已有的备份集与归档文件
ls -la <备份集目录>
ls -la <归档目录>

实测输出:ARCH_MODE = Y,ARCH_INI = 1,归档目录里已有 ARCHIVE_LOCAL1_0x14DB70B5_EP0_2026 文件。ARCH_MODE 是 N 就基本没戏了——归档没开,备份之后的增量无处可寻。

第 3 步:做一次可用的全库备份

-- 在线全库备份(OPEN 状态即可执行)
BACKUP DATABASE FULL BACKUPSET '<备份集目录>';

实测输出:executed successfully,用时 3.085 秒,备份集 16 MB(空库量级),包含 full_G.bak / full_G_1.bak / full_G.meta。备份集格式与 dmrman 完全兼容 —— 备份可以用 SQL 做,恢复照样用 dmrman,两者能对接。

两个必踩的坑:① 备份依赖 DmAPService(DMAP 进程,4236 端口),没起来会报 [-7170] Bakres failed to connect DMAP;② 用 systemd 启动的实例如果没设 WorkingDirectory,备份会报 [-8086] the trace file [../log/...] is invalid,因为相对路径 ../log 解析到了无写权限的目录。

第 4 步:备份之后写的数据,只能靠归档

-- 备份完成后再写入一批数据(模拟正常业务)
INSERT INTO <模式名>.<表名>
  SELECT LEVEL+200, 'CUST_'||LPAD(LEVEL+200,4,'0'), 1000+LEVEL+200
    FROM DUAL CONNECT BY LEVEL <= 50;
COMMIT;

SELECT COUNT(*) AS CNT, SUM(AMT) AS SUM_AMT, MAX(ID) AS MAX_ID
  FROM <模式名>.<表名>;

实测输出:备份集里只有 200 行(SUM=220100),现在表里有 250 行(SUM=281375)。多出的 50 行只能靠归档救。

为什么这步要单独讲:很多恢复失败的案例不是"备份坏了",而是恢复到备份点就以为完事了,把备份之后的增量当成"本来就没有"。有归档却不用,等于白开了归档。

第 5 步:误删后第一件事:把归档切出来

-- 强制把当前归档文件写完并闭卷,开启新的归档文件
ALTER DATABASE ARCHIVELOG CURRENT;

实测输出(切换前后归档目录对比):

切换前:
-rw-r--r--  1 dmdba dinstall 134217728 2026-10-26 22:16 ..._22-10-19.log   ← 预分配 128M,实际只写了 372K
切换后:
-rw-r--r--  1 dmdba dinstall    372736 2026-10-26 22:18 ..._22-10-19.log   ← 闭卷,回落到真实大小
-rw-r--r--  1 dmdba dinstall     8192 2026-10-26 22:18 ..._22-18-17.log   ← 新归档文件开始写

不切的后果(实测踩到过):RECOVER 会报 recover successfully,看起来一切正常,但恢复库里查出来还是备份时的 200 行,备份后的 50 行一行都没有。报"成功"不等于恢复到位,必须靠第 8 步的数据校验来确认。

第 6 步:建恢复库,把全备还原进去

# 1) 用 dminit 建一个空库(端口避开生产库的 5236)
./dminit path=<恢复库目录> DB_NAME=DAMENG INSTANCE_NAME=DR01 PORT_NUM=5237 \
          SYSDBA_PWD=<口令> SYSAUDITOR_PWD=<口令>

# 2) 把全备还原到恢复库(dmrman 必须用 CTLFILE 传命令)
echo "RESTORE DATABASE '<恢复库>/DAMENG/dm.ini' FROM BACKUPSET '<备份集目录>'" > restore.txt
./dmrman CTLFILE=restore.txt

实测输出:restore successfully.,用时 3.871 秒。

dminit 的两个硬性要求:本版 dminit 必须显式给 SYSDBA_PWD 和 SYSAUDITOR_PWD,漏一个就 fail to init db,而且它是逐个提示的 —— 补了 SYSDBA 才告诉你还缺 SYSAUDITOR。另外恢复库的端口要和生产库错开,否则恢复库一起来就抢 5236。

第 7 步:应用归档,恢复到误删前一刻

# 恢复到指定的误删前时间点
echo "RECOVER DATABASE '<恢复库>/DAMENG/dm.ini' \
      WITH ARCHIVEDIR '<归档目录>' \
      UNTIL TIME '2026-10-26 22:19:56'" > recover.txt
./dmrman CTLFILE=recover.txt

实测输出:recover successfully!,用时 457 毫秒 —— 归档量小。归档越多、跨越时间越长,这一步越慢。

只给"恢复前"的时间点还不够:这一次恢复能成立,前提是第 5 步已经把归档切出来。第 5 步 + 第 7 步是一对,缺一个都会退化成"只回到备份点"。

第 8 步:UPDATE DB_MAGIC 并校验数据

# 对齐 db_magic
echo "RECOVER DATABASE '<恢复库>/DAMENG/dm.ini' UPDATE DB_MAGIC" > dbmagic.txt
./dmrman CTLFILE=dbmagic.txt
-- 启动恢复库实例后校验数据
SELECT COUNT(*) AS CNT, SUM(AMT) AS SUM_AMT, MAX(ID) AS MAX_ID
  FROM <模式名>.<表名>;

实测输出:recover successfully!(1.101 秒),校验结果 CNT=250 / SUM=281375 / MIN_ID=1 / MAX_ID=250 —— 与误删前的生产库完全一致,备份后新增的 50 行全部回来了。

不执行 UPDATE DB_MAGIC,恢复库启动会报:[FATAL] Instance DR01 startup failed, execute 'recover database ... update db_magic' in dmrman。

第 9 步:dexp 导出,dimp 导回生产库

# 1) 从恢复库导出目标表(FILE 必须给全路径)
./dexp USERID=SYSDBA/<口令>@localhost:5237 \
       FILE=<全路径>/orders_recovered.dmp \
       TABLES=<模式名>.<表名>

# 2) 导入生产库(5236)
./dimp USERID=SYSDBA/<口令>@localhost:5236 \
       FILE=<全路径>/orders_recovered.dmp \
       TABLES=<模式名>.<表名>

# 3) 生产库校验
SELECT COUNT(*) AS CNT, SUM(AMT) AS SUM_AMT, MIN(ID) AS MIN_ID, MAX(ID) AS MAX_ID
  FROM <模式名>.<表名>;

实测输出:total export 250 rows, size 5.266 KB,导入后 import table ORDERS, has coped with 250 rows,生产库校验 CNT=250 / SUM=281375 / MIN_ID=1 / MAX_ID=250,与误删前完全一致。

dexp / dimp 的 FILE 必须给全路径,只给文件名会在当前工作目录里找。

另一条快路:回收站闪回(前提是它恰好开着)

如果目标实例开了回收站,误删的表可以直接闪回,不用走上面这一整套备份恢复流程。前提是 RECYCLEBIN=1,直接 SHOW RECYCLEBIN 找到原表名后 FLASHBACK TABLE 原表名 TO BEFORE DROP。

微信扫码添加 JC 微信扫码加我
备注「达梦」

卡在某一步了?

先对着 交互版 过一遍,每一步都有实测输出。还对不上,把卡在第几步、报错原文、版本号发我 —— 这三样就够判断了,不用发 dm.ini 或任何配置文件。

邮件:jiang-chuang@qq.com

现象和这几步都对不上?说明可能是版本或环境差异,把已经试过的命令和结果说一下,我能判断是文档该补,还是得单独看。
想让答案别人也能查到?在 Gitee 仓库 提 issue,回复公开可查。

← 返回目录