Redo Log

Redo Log作用:

  • 内存中数据修改后,不必立即更新到磁盘。(比DBWn效率更高)
  • 有日志完成数据的保护目的。(比DBWn效率高)
  • 其他副产品

    • 数据恢复(备份集 + 归档日志)
    • 数据同步(DataGuard、streams、GoldenGate)
    • 日志挖掘

Redo数据流转:

Redo Log 默认有3组,如果开启了归档:当Group 1写满时,会将Group 1归档,然后切换到Group 2。Group 2写满,归档切换Group 3。Group 3写满时, 归档后切换到Group 1。

Redo机制

当用户发出一条SQL语句:update emp set sal=10 where id=1234 时,数据库进行的操作:

例如:1234的sal原值为5,这条数据位于 Data file 2 (该编号即表空间datafile的file id),rollback位于file 3上,其他数据位于 datafile4上。

  1. 1234的emp这个数据块从磁盘中读入内存中(除非它已经存在内存中)
  2. 回滚段数据块紧接着也被读取到内存中(除非它已经在内存中)
  3. 回滚段保存数据的前映像image,即5这个值保存进回滚段的数据块中
  4. 因为回滚段也是数据库数据,所以回滚段的这次insert也会产生一条redo,记录undo数据块的修改操作
  5. 数据的数据块被修改为10
  6. 产生一条redo,记录数据块的修改操作

操作结束后,RedoBuffer中的内容为:

Tran ID File Block Row Column Value 说明
T1 3 12 - - 5 undo产生的redo
T1 2 123 41 6 10 数据块修改的redo
两个事务ID相同,说明这两个操作是同一个事务。 File编号即对应的表空间文件编号 内存中数据块地址 行ID
undo不是表,没有行ID
列ID
undo不是表,没有列ID
数据块的值

redo中只记录了非常有限的信息:

  • 事务ID
  • 列地址(通过File编号、块地址、行ID、列ID进行唯一确定)
  • 被修改的值

此时该update语句还没有进行commit,后续还有其他更新语句。这期间如果另外一个会话操作了另外一张表,可以在前一个用户的操作之间插入redo。例如:

  1. TranID File Block Row Column Value
  2. # A用户执行的update emp set sal=10 where id=1234; sal原值为5
  3. T1 3 12 - - 5
  4. T1 2 123 41 6 10
  5. # B用户执行了 update t set col1='&&&&' where id=111; col1原值为Zack
  6. T87 3 65 - - Zack
  7. T87 4 89 28 22 &&&&
  8. # A用户继续执行 update emp set sal=20 where id=1234;
  9. T1 3 12 - - 10
  10. T1 2 123 41 6 20
  11. # A用户执行了 commit;
  12. T1 comit SCN time stap

commit提交之后,一个commit标识会被记录在redo日志中,内容包括:

  • commit标识
  • 时间戳
  • 当时的SCN

SCN

SCN(System Commit Numer):SCN是数据库中顺序增长的一个数字,用来精确的区别操作的先后顺序,比如commit、rollback or checkpoint。

SCN是一个原子的,每一个事务请求到的SCN都不一样,可以根据SCN的大小判断其先后顺序。

SCN一致性读:用户发出一条SQL后,Oracle会给该SQL一个SCN,例如3259。Oracle在数据库中查找的时候,就只会查找SCN在3259之前的数据块(每个数据块上也都有SCN标记)。在查找的过程中,其他用户对数据库的操作导致SCN的增加,不会影响本次查找的SCN。例如因为用户的操作,SCN增加到了3265,但是Oracle执行该3259的SQL时还是只会查找SCN在3259之前的数据块。如果该数据块已经被修改,则会到回滚段中查找。

日志文件

日志文件使用操作系统块大小:

  • 通常是512 bytes
  • 格式依赖于:操作系统、Oracle版本

Redo日志的组成:

  • 数据头
  • Redo Record | Block0 | Block1 | Block2 | Block3 | ….. | Block M | | —- | —- | —- | —- | —- | —- | | 文件头 | Redo 头 | Redo Record 1 | Redo Record 2或3 | ….. | Redo Record N |

Redo Record记录 的内容:

  • Redo记录头
  • 一个或多个改变向量

每个Redo Record包含每个原子改变的 undo 和 redo。

单行插入的 Redo Record:每一条insert单独插入redo record

  1. Statement #1
    insert into t1 values (1);
    记录的Redo:
    Header        5.2  # 第一次插入时需要插入头
    UNDO #1        5.1
    REDO #1        11.2
    
  1. Statement #2
    insert into t1 values (2);
    UNDO #2        5.1
    REDO #2        11.2
    
  1. Statement #3
    insert into t1 values (3);
    UNDO #3        5.1
    REDO #3        11.2
    
  1. Statement #4
    commit;
    COMMIT        5.4
    

多行插入的Redo Record:等批量的插入完之后记录一条Redo Record

  1. Statement #1
    insert into t1 select * from t2;
    记录的Redo
    Header        5.2
    UNDO #1        5.1
    REDO #1        11.11
    
  1. Statement #2
    commit;
    COMMIT        5.4
    

临时表的数据块不产生Redo:

  1. Statement #1
    inster into t1 values(1);
    记录的redo日志:
    Header        5.2
    UNDO #1        5.1  # undo产生的redo
    # 不产生Redo
    
  1. Statement #2
    insert into t1 values(2);
    记录的redo日志:
    UNDO #2        5.1
    # 不产生REDO
    
  1. Statement #3
    insert into t1 values(3);
    记录的redo日志:
    UNDO #3        5.1
    
  1. Statement #4
    commit;
    记录的redo日志:
    COMMIT        5.4
    

创建临时表:

create global temporary table t_temp(id int);

查看某段时间内的redo日志内容(将redo进行dump,根据scn进行截取):

alter system dump logfile 'D:\app\oradata\orcl\redo01.log' scn min 1280502  scn max 1280520;

重做向量 redo vector

redo vector:redo log存放数据库修改数据的信息。

redo vector里面保存着每个数据块的修改,而不是数据块修改的SQL语句。

日志切换

当LGWR将一个日志文件写满时,将发生日志切换。

为了保证日志文件的安全,应该给每个日志文件增加镜像文件:

# 给redo日志组1增加一个成员
alter database add logfile log1b to group 1;
# 给redo日志组2增加一个成员
alter database add logfile log2b to group 2;

Redo Log总结

Redo Log是Oralce中极其中重要的组件,它的目的在于保证数据的安全性。

Redo Log的丢失可能导致数据库中数据的丢失。

应该将Oracle置于归档模式下。

logminer

用途:对Oracle在线redo、归档日志进行分析。

目的:修正误操作、审计。

日志挖掘

日志挖掘的工具:dbms_logmnr

  • 可以基于日志文件分析(一个或者多个)
  • 可以基于时间段分析
  • 可以基于SCN分析

用法示例:

-- 查看当前正在使用的redo日志
select * from v$log;

-- 获取当前的SCN号
select dbms_flashback.get_system_change_number from dual;

-- 进行一些插入或数据修改的操作
insert into t select rownum from dual connect by rownum<10;
-- 触发LGWR写入redo日志文件中
commit;

-- 查看现在的SCN
select dbms_flashback.get_system_change_number from dual;
-- 将要分析的日志加入logminer
exec dbms_logmnr.add_logfile(LogFileName => 'D:\PC_Software\Program_Software\DataBase_Software\Database_Server\oracle\app\Administrator\oradata\orcl\REDO03.LOG',Options => dbms_logmnr.NEW);
-- 将加入logminer的日志进行日志挖掘
exec dbms_logmnr.start_logmnr(options => dmbs_logmnr.dict_from_online_catalog, StartScn => 1332377, EndScn => 1332389);

-- 查看分析出来的内容
select operation,sql_redo,sql_undo from v$logmnr_contents;

将日志加入logminer时:

第一个加入的redo日志文件,使用 dbms_logmnr.new

如果需要挖掘的日志不仅在这个日志文件中,需要再加入其他的日志文件,后面加入的日志文件需要设置为 dbms_logmnr.addfile

查看v$logmnr_contents时,oracle会现从日志中进行获取,并没有提前获取后保存到该视图。所以如果一个会话执行了dbms_logmnr.start_logmnr,只有在该会话上才能查看v$logmnr_contents,其他会话查看不了。

如果update的记录不存在,则不会产生redo和undo。

-- 如果数据库没有id=100的记录,则不会产生redo和undo
update emp set sal=10 where id=100;

DDL操作这条语句会产生一条redo,其操作的数据不会产生redo。

DDL操作不会生成undo,所以DDL操作不可以被撤销。

alter table t add name varchar2(10);
drop table t1 purge;
truncate table t;

-- 查看挖掘的日志 
select operation, sql_redo, sql_undo from v$logmnr_context where operation='DDL';

v$logmnr_contents有很多字段,但是Oracle默认不会将username等不常用的信息添加进去。如果需要查看这些信息,可以强制加上这些信息:

alter database add supplemental log data;

日志审计

logminer可以用来做日志审计,但是不建议这么做,这么做不太规范,只能做一些应急操作。

通过将日志数据保存到表中,可以对这些日志进行细粒度审计:

create table log_audit as select * from v$logmnr_contents;