目录 搜索

达梦数据文件误删后的恢复 · 交互版

表空间的数据文件被 rm 掉之后,在实例没有重启的前提下,用进程还在持有的文件句柄把文件原样捞回来,再把表空间恢复上线。

单机实例实测 DM8 V8 2026-08-03 前提:实例未重启 8 步全程可复制
Linux /proc 句柄 SP_TABLESPACE_RECOVER 误删 DBF
先分清事故类型:这篇不是「误删表」。本篇处理的是 OS 层把数据文件 .DBF 用 rm 删掉, 靠的是进程还持有文件句柄 —— 命门是实例千万不能重启,重启后句柄失效、文件彻底找不回。
如果你执行的是 DROP TABLE(SQL 层删表、数据文件还在),请走 《达梦利用备份恢复误删表 · 交互版》 两类事故的前提完全相反,走错页面会耽误抢救时机。

操作视频演示

同一套命令、同一份输出,画面与下方步骤卡一一对应。

视频为命令行实录渲染;所有输出取自单机测试库真实执行结果。

七步速成:把误删的数据文件救回来

点左侧任一步骤,右侧给出该步的命令、实测输出与判断依据。
1确认实例状态与数据文件路径
实例必须是 OPEN;记下被删文件的绝对路径,后面每一步都靠它
点击查看命令 →
2确认 dmserver 还持有这个文件
这是整条路的命门:进程没关过文件,才有得救
点击查看命令 →
3误删已发生:立刻停手,不要重启
重启 = 句柄消失 = 这条路彻底断掉
点击查看要点 →
4确认现象:校验前能查,校验后脱机
别被「还能查」骗了,那只是缓存和句柄还在
点击查看命令 →
5把表空间置为恢复态
让数据库准备接受这个表空间的文件回来
点击查看命令 →
6用 /proc 句柄把文件复制回原路径
核心一步:从 /proc/<PID>/fd/<FD> 把 (deleted) 的 inode 拷出来
点击查看命令 →
7执行恢复,校验数据一行不少
恢复后必须验证行数与主键区间,不看日志看数据
点击查看命令 →
步骤明细 已固定

左侧点一步,这里显示该步的完整命令与实测输出。点同一块可取消固定,按 Esc 也可取消。

一句话原理:Linux 上 rm 只是删掉目录项,只要进程还开着这个文件,内核就还留着它的 inode —— 于是能顺着 /proc/<PID>/fd 把它原样拷回来。

先确认实例是 OPEN,再把目标表空间的数据文件路径记下来(后面 6 步都用这个绝对路径)。

-- 实例状态:必须是 OPEN
SELECT INSTANCE_NAME, STATUS$, MODE$ FROM V$INSTANCE;

-- 目标表空间的数据文件绝对路径
SELECT FILE_ID, TABLESPACE_NAME, FILE_NAME
  FROM DBA_DATA_FILES
 WHERE TABLESPACE_NAME = '<表空间名>';
实测输出
LINEID  INSTANCE_NAME  STATUS$  MODE$
1       DM01           OPEN     NORMAL

LINEID  FILE_ID  TABLESPACE_NAME  FILE_NAME
1       0        TEST_RM          /home/dmdba/dmdata/DAMENG/TEST_RM01.DBF

看 dmserver 进程有没有打开这个数据文件。有 fd 指向它,才谈得上救;如果这里什么都没有,直接跳到「边界」章节走备份恢复。

# 取 dmserver 进程号
PID=$(pgrep -x dmserver)
echo $PID

# 看它打开了哪些 fd,过滤出目标数据文件
ls -l /proc/$PID/fd | grep '<数据文件名>'
实测输出(误删之前,符号链接指向真实文件)
$ ls -l /proc/2693/fd | grep TEST_RM
lrwx------ 1 dmdba dinstall 64 Oct 26 21:10 44 -> /home/dmdba/dmdata/DAMENG/TEST_RM01.DBF
记住这个 44,第 6 步要用。

文件已经没了,此刻最重要的不是查原因,而是别让句柄消失。

三条禁令
  1. 不要重启实例(systemctl restart / shutdown / 重启机器都不行)。重启后进程退出,inode 随之释放,句柄法永久失效。
  2. 不要停业务后再慢慢弄——如果在停业务过程中顺手重启了数据库,同样前功尽弃。
  3. 不要往该目录里新建同名文件占位。文件和句柄是两回事,空文件顶替不了原 inode(见「边界」实测)。

正确动作只有三件:立刻拍照当前 fd 列表 → 通知业务方 → 按第 5、6、7 步执行。

误删之后数据库往往看起来正常,这是最容易误判的地方。手动触发一次文件系统校验,真相立刻出来。

-- 校验前:还能查(缓存 + 句柄还在,别当没事)
SELECT COUNT(*) FROM <用户>.<表>;

-- 触发数据文件存在性校验
CALL SP_FILE_SYS_CHECK();

-- 校验后再查:表空间已脱机
SELECT COUNT(*) FROM <用户>.<表>;
实测输出
LINEID  CNT_BEFORE_CHECK
1       1000
used time: 0.932(ms). Execute id is 64701.

SQL> CALL SP_FILE_SYS_CHECK();
DMSQL executed successfully
used time: 2.330(ms). Execute id is 64702.

SQL> SELECT COUNT(*) AS CNT_AFTER_CHECK FROM TEST_RM.T2;
[-3433]:tablespace is offline.
used time: 0.348(ms). Execute id is 0.
注意报错码:本版本(V8 2026-08-03)报的是 [-3433] tablespace is offline;老版本材料里常见的是 [-3430] 文件已被删除。看到哪个都别慌,处理路径相同。

告诉数据库:这个表空间我马上要把文件补回去,先进入恢复准备状态。

CALL SP_TABLESPACE_PREPARE_RECOVER('<表空间名>');
实测输出
SQL> CALL SP_TABLESPACE_PREPARE_RECOVER('TEST_RM');
DMSQL executed successfully
used time: 0.529(ms). Execute id is 64801.

从第 2 步记下的 fd 号,把 (deleted) 的 inode 拷回原路径。这一步决定生死。

# 再取一次进程号(别复用旧变量,防止实例被动重启过)
PID=$(pgrep -x dmserver)

# 找出那个标着 (deleted) 的 fd 号
FD=$(ls -l /proc/$PID/fd | grep '<数据文件名>.*(deleted)' | awk '{print $9}')
ls -l /proc/$PID/fd/$FD

# 原样拷回原路径
cp /proc/$PID/fd/$FD <数据文件绝对路径>
echo "cp 退出码=$?"

# 属主必须与实例进程一致
chown dmdba:dinstall <数据文件绝对路径>
ls -l <数据文件绝对路径>
实测输出
$ ls -l /proc/2693/fd/44
lrwx------ 1 dmdba dinstall 64 Oct 26 21:10 /proc/2693/fd/44 -> /home/dmdba/dmdata/DAMENG/TEST_RM01.DBF (deleted)

$ cp /proc/2693/fd/44 /home/dmdba/dmdata/DAMENG/TEST_RM01.DBF
cp 退出码=0

$ ls -l /home/dmdba/dmdata/DAMENG/TEST_RM01.DBF
-rw-r--r-- 1 dmdba dinstall 134217728 Oct 26 21:11 /home/dmdba/dmdata/DAMENG/TEST_RM01.DBF
校验点:拷回来的字节数必须与原文件一致(本例 134,217,728 = 128MB)。属主/属组不对,实例照样打不开。

文件回来了,还得让数据库认;最后必须用数据本身证明没丢行。

-- 执行恢复
CALL SP_TABLESPACE_RECOVER('<表空间名>');
CALL SP_FILE_SYS_CHECK();

-- 数据完整性:行数 + 主键区间
SELECT COUNT(*) AS CNT_RECOVERED FROM <用户>.<表>;
SELECT MIN(ID) AS MIN_ID, MAX(ID) AS MAX_ID FROM <用户>.<表>;
SELECT TABLESPACE_NAME, STATUS FROM DBA_TABLESPACES
 WHERE TABLESPACE_NAME = '<表空间名>';
实测输出
SQL> CALL SP_TABLESPACE_RECOVER('TEST_RM');
DMSQL executed successfully
used time: 8.769(ms). Execute id is 64901.

SQL> CALL SP_FILE_SYS_CHECK();
DMSQL executed successfully
used time: 44.185(ms). Execute id is 64902.

LINEID  CNT_RECOVERED
1       1000

LINEID  MIN_ID  MAX_ID
1       1       1000
判据:行数回到 1000、主键区间 1–1000 连续,才算真的救回来。只看「执行成功」不算完。

详解:前提、原理与边界

什么时候这条路走得通,什么时候必须改用备份恢复。

一、四个前提,缺一不可

  1. 实例没有重启过。进程活着,inode 才活着。这是最硬的一条。
  2. dmserver 仍然持有该文件的句柄。如果文件在创建后从未被打开过,或者已被数据库关闭,/proc/<PID>/fd 里就找不到它。
  3. 是「删掉」而不是「覆盖」。rm、mv 到其它分区、被清理脚本删掉都算删;如果用 dd 把原文件内容覆盖成 0,inode 还在但数据已经没了,句柄捞回来也是空壳。
  4. 有该主机的 OS 权限。要能读 /proc/<PID>/fd 并能往数据目录写文件,通常就是 dmdba 或 root。

二、为什么「删了还能查」

误删之后立刻 SELECT 往往照样出结果,容易让人以为没事。原因有两层:一是数据页已经在内存缓存里;二是进程还握着文件句柄,底层读并没有断。

只有触发 SP_FILE_SYS_CHECK()(或某些操作主动做文件存在性校验)之后,数据库才会确认「这个路径上没文件了」,把表空间置为脱机。所以「还能查」不等于「没坏」。

三、边界:重启之后就救不了了

实测结论(同一台单机库,第二轮验证)
  1. 再次 rm 数据文件后,/proc/<PID>/fd 仍能看到 (deleted) 句柄;
  2. 执行 systemctl restart 重启实例,进程号从 2693 变为 2482,句柄命中数直接变成 0;
  3. 实例重启后起在 MOUNT 态,任何业务查询都报 [-510] System in mount status——不是单个表空间脱机,而是整个库打不开;
  4. 此时在数据目录里放一个同尺寸的空文件,SP_TABLESPACE_PREPARE_RECOVER / SP_TABLESPACE_RECOVER 都直接报 [-510],无法通过。
也就是说:一旦重启,唯一出路是备份 + 归档恢复,或者从别处重建这个表空间。

四、这条路和「备份恢复」怎么分工

  • 文件级误删(本文):DBF 被删、实例未重启 → 句柄法,分钟级、零备份依赖。
  • 逻辑误删(表 / 行 / 误 UPDATE):数据文件都在,只是数据被删错 → 走备份 + 归档恢复到指定时间点,再把对象导回生产库,见同站《利用备份恢复误删数据》。
  • 两者都不成立(重启 + 无备份):只能重建表空间并从其它数据源补齐,先接受损失再谈恢复。

常见问题

不能。重启后 dmserver 进程退出,内核释放 inode,/proc/<PID>/fd 里的 (deleted) 条目随之消失(实测命中数 0)。此时实例通常起在 MOUNT 态,请直接转备份恢复流程。

看目标位置是否同一个文件系统。同分区内 mv 只是改名,文件根本没被删,句柄不会变成 (deleted),把文件挪回原位即可;跨分区 mv 等于「复制 + 删除」,原 inode 被释放,才能用本文的句柄法。

先别做 SP_TABLESPACE_RECOVER。对比原文件应有的字节数(本例 128MB=134,217,728),确认 cp 是在文件仍被打开期间执行的。若进程已经退出或文件已被关闭,拷到的内容可能不完整,此时不要强行恢复,转备份恢复更安全。

停业务的目的是避免误写扩大影响,但停业务过程中千万不要重启数据库。第 5–7 步都是在线执行的表空间级操作,影响面限于该表空间;是否停业务按现场风险决定,重启则绝对禁止。

让数据库重新校验文件是否存在、是否可用。只跑 SP_TABLESPACE_RECOVER 而不校验,可能拿到「恢复成功」但表空间仍不可用的假象;最后一定要用行数与主键区间证明数据完整。

卡在某一步了?

这一步在你环境里报的错,大概率我也碰过 —— 别自己硬扛。
微信扫码添加 JC
微信扫码加我
备注「达梦」即可
卡住了 / 报错对不上

把卡在第几步、报错原文、版本号发我就够判断了 —— 不用发 dm.ini 或任何配置文件。共性问题我会补进这篇,下一个人就不用再踩这个坑。

这篇没覆盖到

现象和上面几步都对不上?把已经试过的命令和结果说一下,我能判断是文档该补一句,还是版本或环境差异。真要贴参数,先把 IP、路径、口令替换掉。

jiang-chuang@qq.com

想让答案别人也能查到?在 Gitee 仓库 提 issue,回复公开可查。

← 返回指南索引