在数据库的日常运维中,难免会因误操作导致数据丢失。如果数据库没有配置闪回或审计功能,直接采用“备份+归档”的方式虽然能恢复数据,但往往耗时较长,且可能影响业务连续性。此时,我们可以通过挖掘和分析归档日志,快速定位误操作记录,追溯执行该操作的具体业务用户及其来源IP地址,为精准恢复丢失数据和责任追溯提供关键依据。
通过DBMS_LOGMNR挖掘分析达梦数据库的归档日志,核心作用在于实现数据变更的精准追溯与审计。它能够完整还原数据库的历史操作记录,包括DML(增删改)和DDL(表结构变更)语句,帮助DBA快速定位数据误操作、排查业务逻辑错误,并为数据恢复提供关键依据,是保障数据安全与合规性的重要工具。
使用DBMS_LOGMNR对归档日志进行挖掘分析时会造成一定的资源消耗,为避免影响业务系统的正常运行,建议将归档日志拷贝至测试环境进行操作。
本测试场景旨在验证通过DBMS_LOGMNR挖掘分析归档日志后恢复误删数据的可行性:在开启归档及附加逻辑日志参数的前提下,由用户USER01创建业务表并插入测试数据,随后将该表权限授予USER02;通过USER02用户模拟执行 UPDATE和DELETE误操作,造成数据异常丢失;最终利用DBMS_LOGMNR工具对归档日志进行挖掘分析,验证是否能成功捕获误操作事务,从而实现数据的精准恢复。
若需要使用DBMS_LOGMNR挖掘分析源库的归档日志,源库必须开启附加逻辑日志参数,开启后所产生的归档日志才可以进行挖掘分析。
--查询归档是否开启
SELECT ARCH_MODE FROM V$DATABASE;
--开启归档日志
alter database mount;
alter database archivelog;
alter database add archivelog 'DEST=/dmarch, TYPE=LOCAL, FILE_SIZE=256, SPACE_LIMIT=2048';
alter database open;
--查询附件逻辑日志是否开启
SELECT * FROM V$DM_INI WHERE PARA_NAME = 'RLOG_APPEND_LOGIC';
--开启附加逻辑日志
SP_SET_PARA_VALUE(1,'RLOG_APPEND_LOGIC',1);
--RLOG_APPEND_LOGIC参数取值范围及说明
0:不启用;1、2、3、4 启用。
1:如果有主键列,记录 UPDATE 和 DELETE 操作时只包含主键列信息,若没有主键列则包含所有列信息;
2:不论是否有主键列,记录 UPDATE 和 DELETE 操作时都包含所有列的信息;
3:记录 UPDATE 时包含更新列的信息以及 ROWID,记录 DELETE 时只有 ROWID;
4:只生成事务以及 DDL 相关的逻辑日志
注:DELETE 和 UDPATE 操作日志中描述的“所有列的信息”不包含大字段列(如 BLOB 和 CLOB 类型的列)
CREATE TABLESPACE "TBS_USER" DATAFILE 'TBS_USER01.DBF' SIZE 128 AUTOEXTEND ON NEXT 2048;
CREATE USER "USER01" IDENTIFIED BY "Dameng@1234" DEFAULT TABLESPACE "TBS_USER";
GRANT "PUBLIC","RESOURCE","VTI","SOI","SVI" TO "USER01";
CREATE USER "USER02" IDENTIFIED BY "Dameng@1234" DEFAULT TABLESPACE "TBS_USER";
GRANT "PUBLIC","RESOURCE","VTI","SOI","SVI" TO "USER02";
--测试数据
CREATE TABLE USER01.TEST_01(C1 INT,C2 VARCHAR(20));
INSERT INTO USER01.TEST_01 VALUES(1,'测试数据001');
INSERT INTO USER01.TEST_01 VALUES(2,'测试数据002');
INSERT INTO USER01.TEST_01 VALUES(3,'测试数据003');
INSERT INTO USER01.TEST_01 VALUES(4,'测试数据004');
grant SELECT,INSERT,DELETE,UPDATE on USER01.TEST_01 to USER02;
--修改数据
UPDATE USER01.TEST_01 SET C2 = '测试数据003被修改' WHERE C1 = 3;
--删除数据
DELETE FROM USER01.TEST_01 WHERE C1 = 4;
--查询USER01.TEST_01当前结果集
SELECT * FROM USER01.TEST_01;
>SQL backup database full backupset 'D:\dmdbms\data\DAMENG\bak\DB_DAMENG_FULL_2026_0423';
注:分析库必须要通过源库的备份进行还原恢复,因为DBMS_LOGMNR在解析归档日志时需要依赖源库的数据字典信息(表结构、对象ID等),若直接在一个新实例上对源库归档进行挖掘分析,V$LOGMNR_CONTENTS的结果集为空。
[dmdba@localhost bin]$ /dmdbms/bin/dmrman CTLSTMT="RESTORE DATABASE '/dmdata/DAMENG/dm.ini' FROM BACKUPSET '/dmbak/DB_DAMENG_FULL_2026_0423'"
dmrman V8
RESTORE DATABASE '/dmdata/DAMENG/dm.ini' FROM BACKUPSET '/dmbak/DB_DAMENG_FULL_2026_0423'
[Percent:100.00%][Speed:0.00M/s][Cost:00:00:02][Remaining:00:00:00]
restore successfully.
time used: 00:00:03.347
[dmdba@localhost bin]$ /dmdbms/bin/dmrman CTLSTMT="RECOVER DATABASE '/dmdata/DAMENG/dm.ini' FROM BACKUPSET '/dmbak/DB_DAMENG_FULL_2026_0423'"
dmrman V8
RECOVER DATABASE '/dmdata/DAMENG/dm.ini' FROM BACKUPSET '/dmbak/DB_DAMENG_FULL_2026_0423'
[Percent:100.00%][Speed:0.00PKG/s][Cost:00:00:00][Remaining:00:00:00]
recover successfully!
time used: 00:00:03.312
[dmdba@localhost bin]$ /dmdbms/bin/dmrman CTLSTMT="RECOVER DATABASE '/dmdata/DAMENG/dm.ini' UPDATE DB_MAGIC"
dmrman V8
RECOVER DATABASE '/dmdata/DAMENG/dm.ini' UPDATE DB_MAGIC
recover successfully!
time used: 00:00:01.311
[dmdba@~]$ /dmdbms/bin/DmServiceDMSERVER start
Starting DmServiceDMSERVER: [ OK ]
SQL> SELECT DB_MAGIC FROM DUAL;
DB_MAGIC=1912313805
使用/dmdbms/bin目录下的dmmdf工具对归档日志的DB_MAGIC进行修改并保存。
[dmdba@localhost dmsetup]$ /dmdbms/bin/dmmdf TYPE=2 FILE=/dmsetup/ARCHIVE_LOCAL1_0x6054DEB1_EP0_2026-04-24_15-10-26.log
dmmdf V8
**********************************************************
1 sig = DMALOG
2 ver = 7007
3 chksum = 595069911
4 sta = 0
5 n_magic = 0
6 db_magic = 1616174769
7 len = 67108864
8 free = 442368
9 clsn = 176958
10 clsn_fil = 0
11 clsn_off = 4096
12 pemnt_magic = 2147483647
13 fil_id = 0
15 next_seq = 7595
16 g_next_seq = 7595
17 arch_lsn = 176261
18 arch_seq = 7535
19 dbversion = 0x7000d
20 min_exec_version = V8.1.1.1
21 min_dct_version = 4
22 p_db_magic = 0
23 n_apply_ep = 0
24 apply_info_lsn = 0
pkg_seq_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
apply_lsn_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
25 recv_p_db_magic = 0
26 recv_n_apply_ep = 0
recv_pkg_seq_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
recv_apply_lsn_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
27 l_term_id = 0
28 term_id = 0
29 c_seqno = 0
30 c_lsn = 0
31 rpkg_crc_stand = 1
**********************************************************
You can only reset sta(4) or db_magic (6) or clsn (9) or clsn_fil(10) or clsn_off(11) or pemnt_magic(12) or fil_id(13) or next_seq(15) or g_next_seq(16) or p_db_magic(22) or n_apply_ep(23).
Please input the num which one you want to change, q to quit: 6
Input the new value: 1912313805
**********************************************************
1 sig = DMALOG
2 ver = 7007
3 chksum = 852968107
4 sta = 0
5 n_magic = 0
6 db_magic = 1912313805
7 len = 67108864
8 free = 442368
9 clsn = 176958
10 clsn_fil = 0
11 clsn_off = 4096
12 pemnt_magic = 2147483647
13 fil_id = 0
15 next_seq = 7595
16 g_next_seq = 7595
17 arch_lsn = 176261
18 arch_seq = 7535
19 dbversion = 0x7000d
20 min_exec_version = V8.1.1.1
21 min_dct_version = 4
22 p_db_magic = 0
23 n_apply_ep = 0
24 apply_info_lsn = 0
pkg_seq_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
apply_lsn_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
25 recv_p_db_magic = 0
26 recv_n_apply_ep = 0
recv_pkg_seq_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
recv_apply_lsn_arr: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
27 l_term_id = 0
28 term_id = 0
29 c_seqno = 0
30 c_lsn = 0
31 rpkg_crc_stand = 1
**********************************************************
Do you want to quit and save the change to file (y/n): y
Save to file success!
用户可以使用 DBMS_LOGMNR 包对归档日志进行挖掘,重构出DDL和DML 等操作,并通过获取的信息进行更深入的分析(目前DBMS_LOGMNR只支持对归档日志进行分析)。
SQL> SP_CREATE_SYSTEM_PACKAGES(1,'DBMS_LOGMNR');
成功构建DBMS_LOGMNR系统包后,通过DBMS_LOGMNR.ADD_LOGFILE添加归档日志路径。需要注意的是,添加归档日志的操作只能在START之前进行。
SQL> CALL DBMS_LOGMNR.ADD_LOGFILE('/dmsetup/ARCHIVE_LOCAL1_0x6054DEB1_EP0_2026-04-24_15-10-26.log');
--DBMS_LOGMNR.ADD_LOGFILE参数说明
LogFileName:增加的文件的全路径。
Options:可选配置参数(默认为DBMS_LOGMNR.ADDFILE),具体的可选配置包括:
DBMS_LOGMNR."NEW"结束当前LOGMNR(调用 LOGMNR_END),并增加指定文件(如果已经 START,则不可增加);
DBMS_LOGMNR.ADDFILE 在当前 LOGMNR 中增加日志文件(如果已经 START,则不可增加);
DBMS_LOGMNR.REMOVEFILE 从当前 LOGMNR 中去除一个日志文件
RAFTID:主要是针对DPC环境,用于指定RAFT组的RAFT_ID。非DPC环境可以忽略该参数。
通过V$LOGMNR_LOGS动态视图查询已添加的归档日志文件。
SQL> SELECT LOW_SCN,NEXT_SCN,LOW_TIME,HIGH_TIME,LOG_ID,FILENAME FROM V$LOGMNR_LOGS;
通过DBMS_LOGMNR.START_LOGMNR启动归档日志分析。
SQL> DBMS_LOGMNR.START_LOGMNR(OPTIONS=>2130);
注:同一个会话中只能同时启动一个START_LOGMNR,具体报错如下图所示。
通过V$LOGMNR_CONTENTS动态视图查询归档日志的分析结果。
SQL> SELECT * FROM V$LOGMNR_CONTENTS;
注:仅当执行DBMS_LOGMNR.START_LOGMNR();后,才能查询V$LOGMNR_CONTENTS,否则会报错。
SELECT SCN AS "当前记录的LSN",
START_SCN AS "当前事务起始LSN",
COMMIT_SCN AS "当前事务截止LSN",
TIMESTAMP AS "当前记录创建时间",
START_TIMESTAMP AS "当前事务开始时间",
COMMIT_TIMESTAMP AS "当前事务截止时间",
OPERATION AS "操作类型",
OPERATION_CODE AS "操作类型编号",
SQL_REDO AS "SQL语句",
SEG_OWNER AS "被操作模式名",
TABLE_NAME AS "被操作表名",
USERNAME AS "操作者",
SESSION# AS "当前记录会话id",
SESSION_INFO AS "SQL来源的IP"
FROM V$LOGMNR_CONTENTS
WHERE TIMESTAMP BETWEEN '2026-04-24 15:30:00' AND '2026-04-24 15:40:00'
AND SEG_OWNER = 'USER01'
AND TABLE_NAME = 'TEST_01'
AND OPERATION IN ('DELETE',
'UPDATE');
基于3.7.6章节定位到的UPDATE和DELETE误操作记录,编写并执行相应的逆向回滚语句,完成USER01.TEST_01表的数据恢复并查询验证。
UPDATE USER01.TEST_01 SET C2 = '测试数据003' WHERE C1 = 3;
INSERT INTO USER01.TEST_01 VALUES(4,'测试数据004');
COMMIT;
查询USER01.TEST_01结果集。
通过挖掘分析归档日志的结果集,可以看到对USER01.TEST_01进行误操作(修改和删除)的用户是USER02,并且按照《DM8系统管理员手册》中的解释,V$LOGMNR_CONTENTS的SESSION_INFO字段会记录SQL来源的IP地址,但是在测试过程中,该字段并没有记录IP。
在理想情况下(对源库进行误操作的会话连接仍未关闭),可以通过VLOGMNR_CONTENTS中的SESSION#字段存储的当前记录会话ID,到源库中通过VSESSIONS定位IP地址。
SELECT SESS_ID,STATE,CREATE_TIME,CLNT_HOST,APPNAME,CLNT_IP FROM V$SESSIONS WHERE SESS_ID = 2058081336;
若对源库进行误操作的会话连接已经关闭,可以通过VSESSION_HISTORY定位操作用户的信息,VSESSION_HISTORY虽然没有VSESSIONS记录的信息全面(主要区别在于VSESSION_HISTORY不会记录动态改变的信息,例如会话状态、SQL语句等),但V$SESSION_HISTORY会记录用户名、IP等信息。
需要注意的是,查询VSESSION_HISTORY的方式也不是永久有效的,VSESSION_HISTORY里的记录信息存储在内存中,数据库重启后就会清空,记录上限为10000条,当到达上限后会覆盖最旧的历史会话记录。
SELECT SESS_ID,CREATE_TIME,CLNT_HOST,APPNAME,CLNT_IP FROM V$SESSION_HISTORY
WHERE SESS_ID = 2058081336;
SQL> DBMS_LOGMNR.END_LOGMNR();
由于 V$LOGMNR_CONTENTS 仅在执行DBMS_LOGMNR.START_LOGMNR()后才可访问,且随日志分析会话结束而失效,因此可在分析期间将所需数据导出并保存至新建的物理表中。这样即便后续关闭归档分析或结束会话,仍可随时查询和分析历史归档日志的分析结果。
--存放历史归档日志分析结果
CREATE TABLE ARCH_LOG AS
SELECT * FROM
V$LOGMNR_CONTENTS WHERE SEG_OWNER = 'USER_NAME' AND OPERATION_CODE IN (1,2,3,5);
--OPERATION_CODE字段说明
OPERATION_CODE存储SQL操作类型。
1:插入,2:删除,3:修改,4:批量修改,5:DDL操作,6:START,7:COMMIT,36:ROLLBACK,37:SEQ MODIFY,38:XA_COMMIT,255:UNSUPPORTED。
经测试,当RLOG_APPEND_LOGI参数未开启时,DBMS_LOGMN加载与分析归档日志的整个流程均可正常执行,但由于日志中缺乏逻辑操作信息,最终查询 V$LOGMNR_CONTENTS的结果集为空。
当RLOG_APPEND_LOGIC=1时,若数据表存在主键列,执行UPDATE或DELETE操作仅会记录条件列和主键列的值;若数据表不存在主键列,执行UPDATE或DELETE操作会记录所以列的值。
--USER01.TEST_02带主键
CREATE TABLE USER01.TEST_02(C1 INT PRIMARY KEY,C2 VARCHAR(30),C3 VARCHAR(30));
INSERT INTO USER01.TEST_02 VALUES(1,'T2_001','A');
INSERT INTO USER01.TEST_02 VALUES(2,'T2_002','B');
INSERT INTO USER01.TEST_02 VALUES(3,'T2_003','C');
INSERT INTO USER01.TEST_02 VALUES(4,'T2_004','D');
UPDATE USER01.TEST_02 SET C2 = 'T2_001_update' WHERE C1 = 1;
DELETE FROM USER01.TEST_02 WHERE C1 = 2;
--USER01.TEST_03不带主键
CREATE TABLE USER01.TEST_03(C1 INT,C2 VARCHAR(30),C3 VARCHAR(30));
INSERT INTO USER01.TEST_03 VALUES(1,'T3_001','A');
INSERT INTO USER01.TEST_03 VALUES(2,'T3_002','B');
INSERT INTO USER01.TEST_03 VALUES(3,'T3_003','C');
INSERT INTO USER01.TEST_03 VALUES(4,'T3_004','D');
UPDATE USER01.TEST_03 SET C2 = 'T3_001_update' WHERE C1 = 1;
DELETE FROM USER01.TEST_03 WHERE C1 = 2;
数据表存在主键(USER01.TEST_02)
数据表不存在主键(USER01.TEST_03)
结论
建议将RLOG_APPEND_LOGIC设置为2,该取值表示不论数据表是否含有主键列,都会在UPDATE和DELETE操作时记录所有列的值(不包含大字段列),可显著提升日志挖掘的恢复能力,便于找回误删或误改的数据。
文章
阅读量
获赞
