Oracle LogMiner日志挖掘操作总结
一、工具介绍
1.1 什么是LogMiner
Oracle LogMiner 是 Oracle 从 8i 版本开始提供的一个极其强大且免费的内置分析工具。它通过一组 PL/SQL 包(DBMS_LOGMNR和DBMS_LOGMNR_D)和动态视图(如V$LOGMNR_CONTENTS),将 Oracle 重做日志(Online Redo Logs)和归档日志(Archived Logs)中晦涩难懂的底层二进制数据,转化为人类可读的 SQL 语句。
LogMiner工具既可以用来分析在线日志,也可以分析离线日志文件;既可以分析自身数据库的重做日志文件,也可以用来分析其他数据库的重做日志文件。
1.2 LogMiner的作用
LogMiner的四大作用:
| 场景 | 说明 |
|---|---|
| 数据变更审计 | 精准追踪DML操作(INSERT/UPDATE/DELETE)的执行时间、用户及影响范围 |
| 误操作数据恢复 | 当发生错误的DELETE或UPDATE且超出了 Flashback Query 的保留窗口时,通过提取SQL_UNDO语句进行精确回滚。 |
| DDL 变更回溯 | 找回被意外删除或修改的存储过程、表结构等元数据。 |
| 性能与容量分析 | 分析哪些表在特定时间段内被频繁修改,为数据库调优和扩容提供历史依据 |
1.3 LogMiner的主要组件
LogMiner分析工具实际上是由一组PL/SQL包和动态视图组成。
核心PL/SQL包:
| 包名 | 用途 |
|---|---|
DBMS_LOGMNR | 核心分析包,用于添加日志文件、启动/停止LogMiner分析 |
DBMS_LOGMNR_D | 辅助包,用于创建和管理数据字典文件 |
这两个包分别由以下脚本创建:
-- 以SYS用户执行@$ORACLE_HOME/rdbms/admin/dbmslm.sql-- 创建DBMS_LOGMNR包@$ORACLE_HOME/rdbms/admin/dbmslmd.sql-- 创建DBMS_LOGMNR_D包核心动态视图:
| 视图 | 说明 |
|---|---|
V$LOGMNR_DICTIONARY | 显示用于决定对象ID名称的字典文件信息 |
V$LOGMNR_PARAMETERS | 查询当前LogMiner设定的参数 |
V$LOGMNR_LOGS | 在LogMiner启动时显示待分析的日志列表 |
V$LOGMNR_CONTENTS | 最重要的视图,包含解析后的日志内容 |
注意事项:
- 在 19c 多租户(CDB)架构下,LogMiner 必须在CDB 根容器下执行,在 PDB 中执行会报错。
- 所有操作必须在同一个数据库会话(Session)中连续执行。如果断开重连,
V$LOGMNR_CONTENTS视图中的结果将丢失。
1.4 数据字典选项
当LogMiner分析重做数据时,需要一个数据字典将日志中的对象ID转换为可读的表名和列名。如果没有数据字典,LogMiner解释出来的语句中关于数据字典的部分(如表名、列名等)都将是16进制的形式,无法直接理解。
LogMiner提供了三种使用数据字典的方式:
| 方式 | 说明 | 命令 |
|---|---|---|
| 在线目录(Online Catalog) | 使用当前数据库的在线数据字典,必须在源数据库执行 | DBMS_LOGMNR.START_LOGMNR(options=>DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG) |
| 提取到重做日志 | 将字典提取到重做日志流中,要求数据库处于ARCHIVELOG模式且为OPEN状态 | DBMS_LOGMNR_D.BUILD(options=>DBMS_LOGMNR_D.STORE_IN_REDO_LOGS) |
| 提取到操作系统文件 | 将字典导出为文本文件,需设置UTL_FILE_DIR参数 | DBMS_LOGMNR_D.BUILD('filename','directory',DBMS_LOGMNR_D.STORE_IN_FLAT_FILE) |
方式对比:
- 在线目录:最简单,无需额外步骤,但只能分析当前数据库的日志
- 提取到重做日志:适合长期归档分析,字典与日志一起归档
- 提取到文件:适合跨数据库分析,但需要配置
UTL_FILE_DIR并重启数据库
1.5关键前置条件:补充日志 (Supplemental Logging)
在使用 LogMiner 进行数据恢复前,强烈建议开启最小补充日志。
- 未开启时:Oracle 仅记录恢复数据块所需的最小信息。对于
UPDATE操作,LogMiner 可能无法获取修改前的旧值(SQL_UNDO中的旧值可能为NULL),且WHERE条件可能依赖物理地址ROWID,导致生成的回滚 SQL 无法在其他环境执行。 - 开启后:Oracle 会在重做日志中记录足够的列信息(如主键或修改前的值),确保生成的
SQL_REDO和SQL_UNDO逻辑完整、准确可用。
-- 启用最小补充日志 ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; -- 更全面的补充日志(推荐) ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS; -- 验证是否启用 SELECT supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all FROM v$database; SUPPLEMENTAL_LOG_DATA_MIN应返回 'YES' 或 'IMPLICIT'二、简单使用案例
1、补充日志开启确认 SELECT supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui, supplemental_log_data_fk, supplemental_log_data_all FROM v$database; SUPPLEME SUP SUP SUP SUP -------- --- --- --- --- YES YES NO NO NO 2、做一些测试操作 [oracle@host51 ~]$ sqlplus user1/user1 SQL> select count(*) from emp; SQL> select count(*) from dept; COUNT(*) ---------- 4 SQL> insert into emp select * from scott.emp; 14 rows created. SQL> commit; Commit complete. SQL> alter system archive log current; System altered. SQL> delete from dept; 4 rows deleted. SQL> commit; Commit complete. SQL> alter system archive log current; System altered. SQL> create table tab1 as select * from dba_users; Table created. SQL> select to_char(sysdate,'yyyymmdd hh24:mi:ss') from dual; TO_CHAR(SYSDATE,' ----------------- 20260806 15:28:06 3、指定 LogMiner 数据字典(这里我们选择直接从在线目录获取) EXEC DBMS_LOGMNR_D.BUILD(OPTIONS => DBMS_LOGMNR_D.STORE_IN_REDO_LOGS); 4、添加需要挖掘的日志文件 -- 1)生成添加归档日志的执行脚本(根据实际时间范围修改) SELECT DISTINCT 'EXEC DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>''' || name || ''', OPTIONS=>DBMS_LOGMNR.ADDFILE);' AS add_log_sql FROM GV$ARCHIVED_LOG WHERE completion_time BETWEEN TO_DATE('2026-08-06 15:26:00', 'YYYY-MM-DD HH24:MI:SS') AND TO_DATE('2026-08-06 15:29:00', 'YYYY-MM-DD HH24:MI:SS') AND dest_id = 1 ORDER BY 1; -- 2)在 SQL*Plus 中执行上述生成的脚本,批量添加日志 -- (如果是第一个文件,请将 OPTIONS 改为 DBMS_LOGMNR.NEW) 具体如下: EXEC DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'/oradata/orcl/fast_recovery_area/ORCL/archivelog/2026_08_06/o1_mf_1_6_o78ftzoz_.arc', OPTIONS=>DBMS_LOGMNR.new); EXEC DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'/oradata/orcl/fast_recovery_area/ORCL/archivelog/2026_08_06/o1_mf_1_7_o78fvd9b_.arc', OPTIONS=>DBMS_LOGMNR.ADDFILE); 5、启动 LogMiner 会话 EXEC DBMS_LOGMNR.START_LOGMNR(OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG); 6、查询挖掘结果 --由于视图数据量可能极大,且会话已断开,就会丢失视图里面的内容,建议先将结果存入临时表,再进行查询分析 CREATE TABLE t_logminer_res AS SELECT scn,timestamp, operation, table_name, sql_redo, sql_undo FROM v$logmnr_contents WHERE seg_owner = 'USER1' ORDER BY 1; 查询临时表,类似如下 SQL> select * from t_logminer_res; SCN TIMESTAMP OPERATION ---------- ------------ -------------------------------- TABLE_NAME -------------------------------- SQL_REDO -------------------------------------------------------------------------------- SQL_UNDO -------------------------------------------------------------------------------- 1126344 06-AUG-26 INSERT EMP insert into "USER1"."EMP"("EMPNO","ENAME","JOB","MGR","HIREDATE","SAL","COMM","D EPTNO") values ('7369','SMITH','CLERK','7902',TO_DATE('17-DEC-80', 'DD-MON-RR'), '800',NULL,'20'); delete from "USER1"."EMP" where "EMPNO" = '7369' and "ENAME" = 'SMITH' and "JOB" = 'CLERK' and "MGR" = '7902' and "HIREDATE" = TO_DATE('17-DEC-80', 'DD-MON-RR') and "SAL" = '800' and "COMM" IS NULL and "DEPTNO" = '20' and ROWID = 'AAAVVUAAF AAAACDAAA'; ...... 根据找到对应的 SQL_UNDO 或原始的 CREATE/REPLACE PROCEDURE 语句后,交由开发人员确认无误,即可恢复存储过程 7、结束 LogMiner 会话 分析完成后,务必结束会话以释放 PGA/SGA 内存资源: EXEC DBMS_LOGMNR.END_LOGMNR; 至此简单测试案例完成!三、使用案例(详细讲解)
3.1 环境准备与配置
步骤1:确认补充日志已启用
补充日志(Supplemental Logging)必须在生成待分析的重做日志之前启用,否则LogMiner挖掘的一些信息无法正常显示。
-- 查看补充日志状态SELECTsupplemental_log_data_min,supplemental_log_data_pk,supplemental_log_data_ui,supplemental_log_data_fk,supplemental_log_data_allFROMv$database;-- 如果返回结果为NO,执行以下命令启用ALTERDATABASEADDSUPPLEMENTAL LOGDATA;-- 更全面的补充日志(推荐)ALTERDATABASEADDSUPPLEMENTAL LOGDATA(PRIMARYKEY)COLUMNS;步骤2:确认/安装LogMiner包
-- 检查DBMS_LOGMNR包是否存在SELECTowner,object_name,object_typeFROMdba_objectsWHEREobject_nameLIKE'DBMS_LOGMNR%';-- 如果不存在,以SYS用户执行安装脚本@$ORACLE_HOME/rdbms/admin/dbmslm.sql@$ORACLE_HOME/rdbms/admin/dbmslmd.sql步骤3:设置数据字典目录(如使用文件方式)
-- 创建目录CREATEDIRECTORY logminer_dirAS'/u01/app/oracle/logminer';-- 设置UTL_FILE_DIR参数(需要重启数据库)ALTERSYSTEMSETUTL_FILE_DIR='/u01/app/oracle/logminer'SCOPE=SPFILE;-- 然后重启数据库2.2 生成数据字典(文件方式)
如果选择将数据字典提取到操作系统文件(注意需要重启数据库):
-- 创建数据字典文件BEGINDBMS_LOGMNR_D.BUILD(dictionary_filename=>'dictionary.ora',dictionary_location=>'/u01/app/oracle/logminer');END;/[oracle@host51logminer]$ ls-ltr/u01/app/oracle/logminer-rw-r--r-- 1 oracle oinstall 41111786 Aug 6 15:47 dictionary.ora --可以看到数据字典(文件形式)备注:如果不重启数据库,报错如下 ERROR at line1: ORA-01308: initialization parameter utl_file_dirisnotsetORA-06512: at"SYS.DBMS_LOGMNR_INTERNAL",line6110ORA-06512: at"SYS.DBMS_LOGMNR_INTERNAL",line6200ORA-06512: at"SYS.DBMS_LOGMNR_D",line12ORA-06512: at line22.3 添加待分析的日志文件
使用DBMS_LOGMNR.ADD_LOGFILE过程添加需要分析的日志文件:
-- 第一次添加使用NEW选项(清空之前的日志列表)BEGINDBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'/u01/app/oracle/oradata/mydb/redo01.log',OPTIONS=>DBMS_LOGMNR.NEW);END;/-- 继续添加其他日志文件使用ADDFILE选项BEGINDBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'/u01/app/oracle/oradata/mydb/redo02.log',OPTIONS=>DBMS_LOGMNR.ADDFILE);END;/-- 添加归档日志文件BEGINDBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'/u01/app/oracle/archivelog/arch_1_12345.arc',OPTIONS=>DBMS_LOGMNR.ADDFILE);END;/2.4 启动LogMiner
使用DBMS_LOGMNR.START_LOGMNR启动LogMiner分析会话:
方式一:使用在线目录(最常用)
BEGINDBMS_LOGMNR.START_LOGMNR(OPTIONS=>DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);END;/方式二:使用字典文件
BEGINDBMS_LOGMNR.START_LOGMNR(DICTFILENAME=>'/u01/app/oracle/logminer/dictionary.ora',OPTIONS=>DBMS_LOGMNR.DDL_DICT_TRACKING);END;/2.5 查询分析结果
启动LogMiner后,通过查询V$LOGMNR_CONTENTS视图获取解析后的日志内容:
-- 查看所有解析结果SELECTscn,timestamp,username,seg_owner,table_name,operation,sql_redo,sql_undoFROMv$logmnr_contentsORDERBYscn;-- 查看特定用户的操作SELECTscn,timestamp,operation,sql_redo,sql_undoFROMv$logmnr_contentsWHEREseg_owner='USER1'ORDERBYscn;-- 查看特定表的DML操作SELECTscn,timestamp,operation,sql_redo,sql_undoFROMv$logmnr_contentsWHEREseg_owner='USER1'ANDtable_name='EMP'ANDoperationIN('INSERT','UPDATE','DELETE')ORDERBYscn;-- 查看特定时间段的操作SELECTscn,timestamp,username,sql_redoFROMv$logmnr_contentsWHEREtimestampBETWEENTO_DATE('2026-08-01 10:00:00','YYYY-MM-DD HH24:MI:SS')ANDTO_DATE('2026-08-01 12:00:00','YYYY-MM-DD HH24:MI:SS');-- 查看DDL操作SELECTscn,timestamp,username,operation,sql_redoFROMv$logmnr_contentsWHEREoperationLIKE'DDL%';-- 查看已提交的事务(仅显示已提交的变更)SELECTscn,timestamp,operation,sql_redo,sql_undoFROMv$logmnr_contentsWHEREcommit_scnISNOTNULL;2.6 结束LogMiner会话
分析完成后,关闭LogMiner以释放资源:
EXECDBMS_LOGMNR.END_LOGMNR;2.7 完整案例演示
以下是一个完整的LogMiner使用案例:
步骤1:创建测试表并执行操作
-- 创建测试表CREATETABLEtest_logminer(id NUMBER,name VARCHAR2(50),create_timeDATE);-- 插入数据INSERTINTOtest_logminerVALUES(1,'Tom',SYSDATE);INSERTINTOtest_logminerVALUES(2,'Mary',SYSDATE);INSERTINTOtest_logminerVALUES(3,'Mike',SYSDATE);COMMIT;-- 更新数据UPDATEtest_logminerSETname='Tom_Updated'WHEREid=1;COMMIT;-- 删除数据DELETEFROMtest_logminerWHEREid=3;COMMIT;-- 执行DDLALTERTABLEtest_logminerADD(statusVARCHAR2(10));步骤2:强制日志切换
-- 确保日志已写入归档ALTERSYSTEM SWITCH LOGFILE;ALTERSYSTEM SWITCH LOGFILE;步骤3:启动LogMiner分析
-- 添加当前在线日志(或归档日志)-- 1)生成添加归档日志的执行脚本(根据实际时间范围修改)SELECTDISTINCT'EXEC DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'''||name||''', OPTIONS=>DBMS_LOGMNR.ADDFILE);'ASadd_log_sqlFROMGV$ARCHIVED_LOGWHEREcompletion_timeBETWEENTO_DATE('2026-08-06 15:26:00','YYYY-MM-DD HH24:MI:SS')ANDTO_DATE('2026-08-06 15:29:00','YYYY-MM-DD HH24:MI:SS')ANDdest_id=1ORDERBY1;-- 2)在 SQL*Plus 中执行上述生成的脚本,批量添加日志-- (如果是第一个文件,请将 OPTIONS 改为 DBMS_LOGMNR.NEW)EXECDBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'/oradata/orcl/fast_recovery_area/ORCL/archivelog/2026_08_06/o1_mf_1_8_o78fyb2g_.arc',OPTIONS=>DBMS_LOGMNR.new);EXECDBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME=>'/oradata/orcl/fast_recovery_area/ORCL/archivelog/2026_08_06/o1_mf_1_9_o78fybk4_.arc',OPTIONS=>DBMS_LOGMNR.ADDFILE);-- 启动LogMiner(使用在线数据字典)BEGINDBMS_LOGMNR.START_LOGMNR(OPTIONS=>DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);END;/步骤4:查询分析结果
-- 查看测试表的所有变更SELECTscn,TO_CHAR(timestamp,'YYYY-MM-DD HH24:MI:SS')ASop_time,operation,sql_redo,sql_undoFROMv$logmnr_contentsWHEREseg_owner=USERANDtable_name='TEST_LOGMINER'ORDERBYscn;输出示例:
SCN OP_TIME OPERATION ---------- ------------------- -------------------------------- SQL_REDO -------------------------------------------------------------------------------- SQL_UNDO -------------------------------------------------------------------------------- 1155998 2026-08-06 15:57:39 DDL CREATE TABLE test_logminer ( id NUMBER, name VARCHAR2(50), create_time DATE ); 1156010 2026-08-06 15:57:44 INSERT insert into "USER1"."TEST_LOGMINER"("COL 1","COL 2","COL 3") values (HEXTORAW('c 102'),HEXTORAW('546f6d'),HEXTORAW('787e0806103a2d')); delete from "USER1"."TEST_LOGMINER" where "COL 1" = HEXTORAW('c102') and "COL 2" = HEXTORAW('546f6d') and "COL 3" = HEXTORAW('787e0806103a2d') and ROWID = 'AAAV XvAAFAAAACnAAA'; ......步骤5:结束会话
EXECDBMS_LOGMNR.END_LOGMNR;2.8 典型应用场景
场景一:误操作数据恢复
当用户误删除或误更新数据时,可通过LogMiner找到对应的SQL_UNDO语句执行回退:
-- 找到误操作对应的UNDO SQLSELECTsql_undo,timestamp,usernameFROMv$logmnr_contentsWHEREseg_owner='SCOTT'ANDtable_name='EMP'ANDoperation='DELETE'ANDtimestamp>SYSDATE-1/24;-- 最近1小时场景二:数据库变更审计
追踪特定用户或特定时间段的所有数据变更:
SELECTusername,operation,table_name,TO_CHAR(timestamp,'YYYY-MM-DD HH24:MI:SS')ASop_time,sql_redoFROMv$logmnr_contentsWHEREusername='APP_USER'ANDtimestampBETWEENTO_DATE('2025-08-01 00:00:00','YYYY-MM-DD HH24:MI:SS')ANDTO_DATE('2025-08-01 23:59:59','YYYY-MM-DD HH24:MI:SS')ORDERBYtimestamp;场景三:分析数据增长模式
通过分析INSERT操作,了解数据增长趋势:
SELECTTO_CHAR(timestamp,'YYYY-MM-DD')ASday,COUNT(*)ASinsert_count,seg_owner,table_nameFROMv$logmnr_contentsWHEREoperation='INSERT'ANDtimestamp>SYSDATE-30GROUPBYTO_CHAR(timestamp,'YYYY-MM-DD'),seg_owner,table_nameORDERBYdayDESC;