Oracle的日志挖掘,是一种数据恢复技术,只需要你的归档日志(Archivelog)和重做日志(redo log)完整,即可将数据恢复到你想要的任意时刻。在这里,就让我首先说说重做日志挖掘。

先来说说redo。

redo日志挖掘是基于重做日志(redo log)的基础之上进行的挖掘恢复操作。那么,做为Oracle的重要特性之一的redo,它用来保证事务的可恢复和可回退。也就是说,它可以重演事务,从而使得在数据库发生故障之后,数据可以被恢复。redo对于Oracle数据库来说是相当重要的。

redo的三大件 redo log buffer、LGWR、redo log file。所以,redo的功能主要通过这三大组件来实现。

redo log buffer重做日志缓冲区是Oracle数据库SGA中最小的一个内存结构。重做日志缓冲区是一个循环使用的内存缓冲区,它以重做日志条目(redo log entries)的形式存储。当它写满时,新的重做日志条目(redo log entries)将从缓冲区的起始位置开始写入,覆盖旧的数据。

重做日志条目(redo log entries)是由用户进程生成的,它包含了重构、重做数据库变更的重要信息,这些变更包括INSERT,UPDATE,DELETE,CREATE,ALTER 或者 DROP 等。在必要的时候重做日志条目(redo log entries)被用于数据库恢复。当用户进程执行这些操作时,会生成相应的重做日志条目(redo log entries),所以这些条目中记录了执行的数据操作、事务信息等 。

当用户进程生成重做日志条目(redo log entries),首先被存储在重做日志缓冲区(redo log buffer)中[PGA-->SGA],然后由LGWR(log writer)进程将重做日志缓冲区(redo log buffer)中的数据写入到磁盘上的在线重做日志组(online redo log)中。这个过程,保证了数据的一致性和持久性,即使数据库发生故障,也可以通过重做日志文件(redo log file)来恢复数据。

redo log buffer是循环使用的,当LGWR进程不断地把redo log buffer的内容写出到redo log file中,写入到online redo log中。redo log file同样是循环使用的。

LGWR(log writer)后台进程,将日志缓冲内容写到磁盘的在线重做日志文件或组(online redo log)中。DBWR则是将数据块分散写入磁盘(落盘)。

当然,以上的过程,都是在数据commit时,将变更写入在线重做日志(online redo log)中。

基本就这么个意思了。

其它相关的还有SGA中的种池、DBWN、检查点、归档,在这里不详述。以上说了这么说,无非就是要告诉你,redo的基本操作,它这一趟子走下来,最终把数据丢哪儿了。然后,接下来,我们找东西。

挖掘过程是这样的。

当数据库在运行过程中,会发生许许多多的操作,当然包括以上的INSERT,UPDATE,DELETE,CREATE,ALTER 或者 DROP 等,而所有的这些操作都会被记录在redo日志中。redo日志也记录undo日志,所以也可使用redo日志中的undo日志,对所做的操作进行回退。那么,只要明确了要恢复的操作记录在哪个时间范围内,就可以通过时间或者scn号将要恢复的操作写入到v$logmnr_contents视图中,然后找到对应的undo记录,执行undo记录的sql语句,就可将数据恢复到更改前的状态。

Oracle-redo日志挖掘_redo

0.测试数据

create table baoyw (id int,name varchar(60));
insert into baoyw values (1,'北京’);
insert into baoyw values (1,'北京’);
insert into baoyw values (1,'北京’);
commit;

1.格式化当前时间显示

SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';

Session alerted.

SQL>

2.查看系统是否运行在归档模式

SQL> archive log list;
Database log mode               Archive Mode
Automatic archival              Enabled
Archive destination             /oracle/app/oracle/archive/
Oldest online log sequence      3549
Next log sequence to archive    3551
Current log sequence            3551
SQL>

如果没有开启归档,在mount下开启归档

3.启用最小日志。

--开启最小日志
alter database add supplemental log data;
--
alter database add supplemental log data (primary key) columns;
--
alter database add supplemental log data (unique) columns;
--
alter database add supplemental log data (primary key, unique index) columns;

4.查询日志状态 NO关闭 YES开启

--默认开启最小日志
SQL> 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      NO  NO  NO  NO

SQL>

5.修改一条数据

SQL> update baoyw set name = '重庆' where id = '2';

1 row updated.

SQL> commit;

Commit complete.

SQL> select sysdate from dual;

SYSDATE
-------------------
2024-03-11 18:20:30

SQL> select * from baoyw;
         ID NAME
----------- ---------------------------------
          1 北京
          2 重庆
          3 宁夏
          
SQL>

6.开启日志挖掘

将需要挖掘的时间段的日志,写入到v$logmnr_contents视图中。

使用在线字典自动加载。

以时间范围加载日志。

--开启日志挖掘
--调用相应的存储过程,除时间外,其他都是固定参数
SQL> begin
 2 dbms_logmnr.start_logmnr(
 3 starttime=>to_date('2024-03-11 18:20:00','yyyy-mm-dd hh24:mi:ss'),
 4 endtime=>to_date('2024-03-11 18:22:14','yyyy/mm/dd hh24:mi:ss'),
 5 options=>dbms_logmnr.dict_from_online_catalog + dbms_logmnr.continuous_mine);
 6 end;
 7 /

PL/SQL procedure successfully completed.

SQL>

7.手动切换归档

手动切换可有可无。

--手动切换归档 将redo记录写入到datafile,并形成归档
alter system checkpoint;
alter system switch logfile;

8.查询视图 v$logmnr_contents  

sql_redo 是你操作过程的关键字。

如果只通过seg_name条件,将得到一定时间范围内,此用户的所有操作。

seg_name 是更改的表名,seg_owner是表所属的用户,sql_redo是redo日志记录的sql信息。

sql_undo 就是你的回退sql 用这条sql就可以将数据恢复到更改前的状态。

SQL>SELECT sql_undo
 2 FROM v$logmnr_contents
 3  AND seg_name = 'BAOYW'
 4  AND sql_redo LIKE 'update%';
    
SQL_UNDO
------------------------------------------------------------------------------
update "SYS"."BAOYW" set "NAME" = '天津' where "ID" = '2' and "NAME" = '重庆‘
  and ROWID = 'aaawG9AABAAAXspAAB';
  
SQL>

9.执行undo字段中的sql

SQL> update "SYS"."BAOYW" set "NAME" = '天津' where "ID" = '2' and "NAME" = '重庆‘ and ROWID = 'aaawG9AABAAAXspAAB';

1 row updated.

SQL> select * from baoyw;

         ID NAME
----------- ---------------------------------
          1 北京
          2 天津
          3 宁夏
          
SQL> commit;

Commit complete.

SQL>

10.关闭日志挖掘

退出当前会话,即可关闭日志挖掘。

--关闭日志挖掘
begin
dbms_logmnr.end_logmnr;
end;
/

11.其他

--日志
 SELECT  GROUP#,
      THREAD#,
      SEQUENCE#,
      BYTES,
      BLOCKSIZE,
      MEMBERS,
      ARCHIVED,
      STATUS, FIRST_CHANGE#, to_char(FIRST_TIME, 'yyyy-mm-dd hh24:mi:ss') FIRST_TIME, NEXT_CHANGE#, to_char(NEXT_TIME, 'yyyy-mm-dd hh24:mi:ss') NEXT_TIME
FROM v$log;
col MEMBER for a65
select * from v$logfile;
select * from v$log;
select * from v$archived_log;