重做(redo)
重做(redo)和撤消(Undo)是 Oracle 的重要特性,用以保证事务的可恢复性和可回退
性。本章对 Oracle 的重做机制进行说明。
7.1Redo 的作用
Oracle 通过 redo 来保证数据库的事务可以被重演,从而使得在故障之后,数据可以被恢
复。Redo 对于 Oracle 数据库来说至关重要。
在数据库中,Redo 的功能主要通过三个组件来实现:Redo Log Buffer 、LGWR 后台进程
和 Redo Log File(在归档模式下,Redo Log File最终会经由ARCn进程写出为归档日志文件)。
图 7-1 是 Oracle 的数据库实例示意图,Redo Log Buffer 以及 LGWR 进程在图中皆有体现:
图 7-1
Redo Log Buffer 位于 SGA 之中,是一块循环使用的内存区域。其中保存数据库变更的相
关信息。这些信息以重做条目(Redo Entries)形式存储(Redo Entries 也经常被称为 Redo
records )。 Redo Entries 包含重构、重做数据库变更的重要信息,这些变更包括
INSERT,UPDATE,DELETE,CREATE,ALTER 或者 DROP 等。在必要的时候 Redo Entries 被用
于数据库恢复。
Redo Entries 的内容被 Oracle 数据库进程从用户的内存空间(PGA)复制到 SGA 中的 Redo
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·2·
Log Buffer 之中。Redo Entries 在内存中占用连续的顺序空间,由于 Redo Log Buffer 是循环使
用的,Oracle 通过一个后台进程 LGWR 不断的把 Redo Log Buffer 的内容写出到 Redo Log File
中,Redo Log File 同样是循环使用的。图 7-2 说明了 Redo Log Buffer、LGWR 以及 Redo Log File
三者之间的关系。
图 7-2
7.2Redo 的原理
通过前面的章节,我们已经知道,用户数据通常在 Buffer Cache 中修改,Oracle 通过高速
缓存来ᨀ高数据操作的性能。当用户在 Buffer Cache 中修改数据时,Oracle 并不会立即将变
更数据写出到数据文件上,因为独立的离散写出效率会很低。到目前为止,计算机系统中最
容易出现瓶颈的仍然是磁盘的 I/O 操作,Oracle 这样做的目的是为了减少 IO 的压力,当修改
过的数据达到一定数量之后,可以进行高效地批量写出。
大部分传统数据库(当然包括 Oracle)在处理数据修改时都遵循 no-force-at-commit 策略。
也就是说,在ᨀ交时并不强制写。那么为了保证数据在数据库发生故障时(例如断电)可以恢复,
Oracle 引入了 Redo 机制,通过连续的、顺序的日志条目的写出将随机的、分散的数据块的写
出推延。这个推延使得数据的写出可以获得批量效应等性能ᨀ升。
同 Redo Log Buffer 类似,Redo Log File 也是循环使用的,Oracle 允许使用最少两个日志
组。缺省的,数据库创建时会建立 3 个日志组。
SQL> select group#,members,status from v$log;
GROUP# MEMBERS STATUS
第 1 章 章名章名章名章名章名
·3·
---------- ---------- ----------------
1 1 INACTIVE
2 1 CURRENT
3 1 INACTIVE
当一个日志文件写满之后,会切换到另外一个日志文件,这个切换过程称为 Log Switch。
Log Switch 会触发一个检查点,促使 DBWR 进程将写满的日志文件保护的变更数据写回到数
据库。在检查点完成之前,日志文件是不能够被重用的。
由于 Redo 机制对于数据的保护,当数据库发生故障时,Oracle 就可以通过 Redo 重演进
行数据恢复。那么一个非常重要的问题是,恢复应该从何处开始呢?
如果读取的 redo 过多,那么必然导致恢复的时间过长,在生产环境中,我们必需保证恢
复时间尽量的短。
Oracle 通过检查点(Checkpoint)来缩减恢复时间。
回顾一下我们在第一章中所ᨀ到的:检查点只是一个数据库事件,它存在的根本意义
在于减少恢复时间。
当检查点发生时(此时的 SCN 被称为 Checkpoint SCN)Oracle 会通知 DBWR 进程,把修
改过的数据,也就是此 Checkpoint SCN 之前的脏数据(Dirty Buffer)从 Buffer Cache 写入磁
盘,在检查点完成后 CKPT 进程会相应的更新控制文件和数据文件头,记录检查点信息,标
识变更。
在检查点完成之后,此检查点之前修改过的数据都已经写回磁盘,重做日志文件中的相应
重做记录对于崩溃/实例恢复不再有用。
如果此后数据库崩溃,那么恢复只需要从最后一次完成的检查点开始恢复即可。
如果数据库运行在归档模式(所有生产数据库,都建议运行在归档模式),日志文件在重
用之前必须写出到归档日志文件,归档日志在介质恢复时可以用来恢复数据库故障。
7.3Redo 与 Latch
在一个多用户的并发系统中,大量用户进程都需要向 Redo Log Buffer 写入重做数据,
Oracle通过Latch来保护和协调Redo Log Buffer的工作。同Redo相关的Latch主要有Redo Copy
Latch、Redo Allocation Latch 等,Redo Allocation Latch 用于管理 Log Buffer 内存空间的分配,
Redo Copy Latch 则用于写 Redo 内容到 Redo Log Buffer 过程的保护。
一个进程在修改数据时产生 Redo,Redo 首先在 PGA 中保存,当进程需要将 Redo 信息
Copy 进入 Redo Log Buffer 时需要获得 redo copy latch,获得了该 latch 以后才能把 redo 拷贝到
Log Buffer 中。Redo copy latch 表明进程正在把 redo 拷贝入 log buffer 中,在此过程中,LGWR
应该等待直到进程拷贝完成才能把目标 Log buffer Block 写入磁盘。
初始化参数_LOG_SIMULTANEOUS_COPIES,定义允许同时写redo的redo copy latch的数
量。在 Oracle7 和 Oracle8 里,_LOG_SIMULTANEOUS_COPIES 缺省的等于 CPU 的数量。
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·4·
从 Oracle8.1.3 开始,缺省的_LOG_SIMULTANEOUS_COPIES 变成 2 倍的 CPU 数量,并且成
为了一个隐含参数:
SQL> @GetHidPar
Enter value for par: copies
NAME VALUE DESCRIB
----------------------- ----- --------------------------------------------------------
_log_simultaneous_copies 8 number of simultaneous copies into redo buffer(# of copy latches)
从 v$latch 视图中我们可以得到关于 redo copy latch 的汇总信息:
SQL> select name,GETS,IMMEDIATE_GETS,IMMEDIATE_MISSES,SPIN_GETS
2 from v$latch where name='redo copy';
NAME GETS IMMEDIATE_GETS IMMEDIATE_MISSES SPIN_GETS
--------------- ---------- -------------- ---------------- ----------
redo copy 112 247810971 7184 0
对于 redo copy latch 的多个子 Latch,可以从 v$latch_children 视图获得更为详细的信息:
SQL> select addr,latch#,child#,name,gets,immediate_gets,immediate_misses
2 from v$latch_children where name = 'redo copy';
ADDR LATCH# CHILD# NAME GETS IMMEDIATE_GETS IMMEDIATE_MISSES
-------- ---------- ---------- --------------- ---------- -------------- ----------------
57187FB4 147 1 redo copy 14 118062595 4199
57188030 147 2 redo copy 14 40781669 912
571880AC 147 3 redo copy 14 68951827 1807
57188128 147 4 redo copy 14 1557929 133
571881A4 147 5 redo copy 14 13341465 105
57188220 147 6 redo copy 14 5115486 28
5718829C 147 7 redo copy 14 0 0
57188318 147 8 redo copy 14 0 0
8 rows selected.
Redo copy latch 获取以后,进程紧接着需要获取 redo allocation latch ,分配 redo 空间,空
间分配完成以后,redo allocation latch 即被释放,进程把 PGA 里临时存放的 redo 信息 COPY
入 redo log buffer,COPY 完成以后,redo copy latch 释放。
在完成 redo copy 以后,进程可能需要通知 LGWR 去执行写出(如果 redo copy 是 commit
等因素触发的)。为了避免 LGWR 被不必要的通知,进程需要先获取 redo writing latch 去检查
LGWR 是否已经激活或者已经被通知。如果 LGWR 已经激活或被 Post,redo writing latch 将被
释放。
SQL> col name for a20
SQL> select addr,latch#,name,gets,misses,immediate_gets,immediate_misses
2 from v$latch where name='redo writing';
ADDR LATCH# NAME GETS MISSES IMMEDIATE_GETS IMMEDIATE_MISSES
-------- ---------- -------------------- ---------- ---------- -------------- -----------
第 1 章 章名章名章名章名章名
·5·
0217ECC8 113 redo writing 2265 0 0 0
如果 redo writing latch 竞争过多,可能意味着你的ᨀ交过于频繁。通过系统统计信息或
Statspack 可以获得这些信息,具体参考 "log file sync" 等待事件一节。
在执行 redo copy 的过程中,进程以 log file sync 事件处于等待。当进程从 log file sync 中
等待中醒来以后,进程需要重新获得 redo allocation latch 检查是否相应的 redo 已经被写入 redo
log file,如果尚未写入,进程必须继续等待。这是一个大致简化的 Latch 处理过程,用以说明
Latch 的处理机制。
和 Redo 相关的另外一个常见 Latch 是 redo allocation latch,当进程需要向 Redo Log Buffer
写入 Redo 信息时需要获得此 Latch,分配 Redo Log Buffer 空间。所以,如果对于一个繁忙的
数据库系统,该 Latch 通常也是竞争激烈的 Latch 之一。在以上取样的数据库中,Redo allocation
latch 和 Redo Copy Latch 同属 Top 5 请求的 Latch 之一:
NAME IMMEDIATE_GETS IMMEDIATE_MISSES SPIN_GETS
----------------------------------- -------------- ---------------- ----------
cache buffers lru chain 259891274 209819 213249
cache buffers chains 258525736 1470 18065
redo copy 247810939 7184 0
redo allocation 247808297 9909 926
checkpoint queue latch 56443129 4945 3825
7.4Oracle9i Redo 的增强
在 Oracle9iR2 中,Oracle 通过 LOG_PARALLELISM 定义 Oracle 中 redo allocation 的并发
级别。如果定义 LOG_PARALLELISM 大于 1,那么数据库将分配多个共享的 Redo Log Buffer
区域,每个共享 Buffer 都有独立的 Redo Allocation Latch 进行保护,从而ᨀ高了 Redo 的并发
性能,多个 Redo Log Buffer 可以被看作是 Redo Log Buffer 的子池,与 Shared Pool 的多 Subpool
原理类似。多 Redo Log Buffer机制又被称为 Public Redolog Strands。图 7-3是新机制下 Redo Log
Buffer 原理示意图:
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·6·
图 7-3
如果你使用的是高端服务器,有超过 16 个处理器,并且经历非常高的 redo allocation latch
竞争,那么可以考虑启用并行 redo。允许并行 redo 生成能够增加更新密集型数据库的吞吐量,
可以通过考察 V$LATCH 视图观察 redo allocation latch 竞争的累计等待时间。
通过如下查询可以获得相关 Latch 信息:
SELECT substr(ln.name, 1, 20), gets, misses, immediate_gets, immediate_misses
FROM v$latch l, v$latchname ln
WHERE ln.name in ('redo allocation', 'redo copy') and ln.latch# = l.latch#;
如果MISSES对GETS比率超过1%,或者IMMEDIATE_MISSES 对 (IMMEDIATE_GETS
+ IMMEDIATE_MISSES) 比率超过 1%,那么通常认为存在 Latch 竞争。
当主机拥有 16~64 个 CPU 时,Oracle 公司推荐设置 LOG_PARALLELISM 在 2~8 之间。
你可以从低值(例如 2)开始,以 1 为步长增进直到 redo allocation latch 竞争不再激烈,这个参
数的设置可以ᨀ高应用的性能。大于 8 的 LOG_PARALLELISM 设置通常不被推荐。
在 Oracle9iR2 中,该参数的缺省值为 1:
SQL> show parameter log_p
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_parallelism integer 1
缺省的 Redo Allocation Latch 也仅有一个:
SQL> select * from v$version where rownum <2;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
SQL> select addr,latch#,child#,name,gets,immediate_gets,immediate_misses
2 from v$latch_children where name = 'redo allocation';
ADDR LATCH# CHILD# NAME GETS IMMEDIATE_GETS IMMEDIATE_MISSES
-------- ---------- ---------- --------------- ---------- -------------- ----------------
5343A908 115 1 redo allocation 183268 0 0
在 Oracle10gR2 中,这一切又有所改变。
7.5Oracle10g Redo 的增强
在 Oracle0g 中,log_parallelism 参数变为隐含参数,并且 Oracle 引入了另外两个参数,允
许 log_parallelism 进行动态调整。缺省的_log_parallelism_dynamic 参数被设置为 True,如果
_log_parallelism_max 被设置为不同于_log_parallelism 的参数值,那么 Oracle 会动态的选择并
行度,当然不超过最大允许值,这是 Oracle10g 中动态 SGA 的另外一个ᨀ高。
SQL> @GetHparDes.sql
Enter value for par: log_parallelism
第 1 章 章名章名章名章名章名
·7·
old 6: AND x.ksppinm LIKE '%&par%'
new 6: AND x.ksppinm LIKE '%log_parallelism%'
NAME VALUE DESCRIB
------------------------------ -------------------- ------------------------------------
_log_parallelism 1 Number of log buffer strands
_log_parallelism_max 2 Maximum number of log buffer strands
_log_parallelism_dynamic TRUE Enable dynamic strands
但是需要注意的是当日志并行度被设置大于 1 之后,LogMiner 将不能解析日志文件,
ORA-01374 号错误ᨀ示说明了这个问题:
ORA-01374: _log_parallelism_max greater than 1 not supported in this release
Cause: LogMiner does not mine redo records generated with log_parallelism set to a value greater
than 1
Action: none
相较 Oracle9i 中的 Public Redolog Strands(缩写为 PBRS),Oracle10gR2 中更引入了 Private
Redolog Strands 的概念(缩写为 PVRS),在 PVRS 机制下,数据库可以在共享池中分配大量
的小的私有内存,通常每个大小在 64~128K 左右,被独立的 Redo Allocation Latch 所保护,
当数据库中特定类型的小事务开始时会被绑定到独立且空闲 PVRS,每个 Buffer 绑定一个活动
事务。在新的机制下,Redo 产生后可以直接存入 PVRS,而不再保存在 PGA 中,这样就不再
需要额外的内存拷贝过程,Redo Copy Latch 也就不再需要(PVRS 也因此被称为 ZERO-COPY
Redo),而 Redo Copy 正是引发 Redo Allocation Latch 竞争的根源。
图 7-4 说明了在不断改进后的 Redo 以及 Log Buffer 原理:
图 7-4
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·8·
新的机制下,在进行 Redo 写出工作时,LGWR 需要做的工作就是将 PBRS 和 PVRS 中的
内容写出,当 Redo Flush 发生时,所有的 Public Redo Allocation Latch 需要被获取,所有 Public
Strands 的 Redo Copy Latch 需要被检查,所有包含活动事务的 Private Strands 需要被持有。
以下是 PVRS 在共享池中的内存分配信息(注意在 RAC 环境中不适用 PVRS):
SQL> select banner from x$version where indx in (0,3);
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
TNS for Linux: Version 10.2.0.1.0 - Production
SQL> select * from v$sgastat where name='private strands';
POOL NAME BYTES
------------ -------------------------- ----------
shared pool private strands 4060160
在 Oracle10gR2 中,Oracle 通过使用多个 Redo Allocation Latch 来ᨀ高并发性能:
SQL> select addr,latch#,child#,name,gets,immediate_gets,immediate_misses
2 from v$latch_children where name = 'redo allocation';
ADDR LATCH# CHILD# NAME GETS IMMEDIATE_GETS IMMEDIATE_MISSES
-------- ------ ------ -------------------- ---------- -------------- ----------------
573EC7BC 148 1 redo allocation 569765 203606724 9329
573EC820 148 2 redo allocation 373552 44202744 580
573EC884 148 3 redo allocation 109793 0 0
573EC8E8 148 4 redo allocation 56855 0 0
573EC94C 148 5 redo allocation 24753 0 0
573EC9B0 148 6 redo allocation 203959 0 0
573ECA14 148 7 redo allocation 6352 0 0
573ECA78 148 8 redo allocation 6352 0 0
573ECADC 148 9 redo allocation 6352 0 0
573ECB40 148 10 redo allocation 6352 0 0
573ECBA4 148 11 redo allocation 6352 0 0
573ECC08 148 12 redo allocation 6352 0 0
573ECC6C 148 13 redo allocation 6352 0 0
573ECCD0 148 14 redo allocation 6352 0 0
573ECD34 148 15 redo allocation 6352 0 0
573ECD98 148 16 redo allocation 6352 0 0
573ECDFC 148 17 redo allocation 6352 0 0
573ECE60 148 18 redo allocation 6352 0 0
573ECEC4 148 19 redo allocation 6352 0 0
573ECF28 148 20 redo allocation 6352 0 0
20 rows selected.
第 1 章 章名章名章名章名章名
·9·
以下是与 PVRS 相关的几个隐含参数,缺省的_log_private_parallelism 被设置为 FALSE:
SQL> @GetHidPar
Enter value for par: log_private
NAME VALUE PDESC
---------------------------- ----- ------------------------------------------------------
_log_private_parallelism FALSE Number of private log buffer strands for zero-copy redo
_log_private_parallelism_mul 10 Active sessions multiplier to deduce number of private
strands
_log_private_mul 5 Private strand multiplier for log space preallocation
PRVS 的统计数据可以从 V$SYSSTAT 视图查询得到,由于 IMU(In Memory Undo)与
PRVS 紧密相关,所以两者的信息具有相关性(IMU 在下一章中介绍):
SQL> select name,value from v$sysstat where upper(name) like '%IMU%';
NAME VALUE
---------------------------------------- ------------------------
doubling up with imu segment 0
IMU commits 26346
IMU Flushes 11391
IMU contention 12
IMU recursive-transaction flush 9
IMU undo retention flush 0
IMU ktichg flush 0
IMU bind flushes 0
IMU mbu flush 0
IMU pool not allocated 4504
IMU CR rollbacks 222
IMU undo allocation size 78339248
IMU Redo allocation size 8012716
IMU- failed to get a private strand 4504
OS Maximum resident set size 0
在实施了 PRVS 之后,在告警日志信息中可能看到如下信息,这里的 Private_strands 就是
新特性引入的新的ᨀ示:
Mon Sep 26 13:29:25 2005
Private_strands 3 at log switch
Thread 1 advanced to log sequence 58
Current log# 3 seq# 58 mem# 0: /u01/oradata/onlinelog/o1_mf_3_1m7lp0ht_.log
Current log# 3 seq# 58 mem# 1: /u01/oradata/onlinelog/o1_mf_3_1m7lp0rp_.log
Thread 1 cannot allocate new log, sequence 59
Checkpoint not complete
Current log# 3 seq# 58 mem# 0: /u01/oradata/onlinelog/o1_mf_3_1m7lp0ht_.log
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·10·
Current log# 3 seq# 58 mem# 1: /u01/oradata/onlinelog/o1_mf_3_1m7lp0rp_.log
更进一步的如果收到ᨀ示 Private strand flush not complete,这是指从内存到 Redo Log File
的写出尚未完成:
Fri May 19 12:47:29 2006
Thread 1 cannot allocate new log, sequence 18358
Private strand flush not complete
Current log# 7 seq# 18357 mem# 0: /u03/oradata/bitst/redo07.log
Thread 1 advanced to log sequence 18358
Current log# 8 seq# 18358 mem# 0: /u03/oradata/bitst/redo08.log
通过 V$EVENT_NAME 可以找到关于这个新等待的说明:
SQL> select name from v$event_name where upper(name) like '%STRAND%';
NAME
------------------------------------------------------------
log file switch (private strand flush incomplete)
7.6Redo 的内容
大家知道,Oracle 通过 Redo 来实现快速ᨀ交,一方面是因为 Redo Log File 可以连续、顺
序的快速写出,另外一个方面也和 Redo 记录的精简内容有关。
为了了解 Redo 的内容,需要了解两个概念:
1. 改变向量(Change Vector)
改变向量表示对数据库内某一个数据块所做的一次变更。改变向量(Change Vector)
中包含了变更的数据块的版本号、事务操作代码、变更从属数据块的地址(DBA)以及
更新后的数据。
例如一个 Update 事务包含一系列的改变向量,对于数据块的修改是一个向量,对于
回滚段的修改又是一个向量。
2. 重做记录(Redo Record)
重做记录通常由一组改变向量组成,是一个改变向量的集合,代表一个数据库的变更
(insert、Update、Delete 等操作),构成数据库变更的最小恢复单位。
例如一个 Update 的重做记录包括相应的回滚段的改变向量和相应的数据块的改变向
量等。
下面以一个更新(Update)操作为例介绍一下这个过程,如图 7-5 所示。
第 1 章 章名章名章名章名章名
·11·
图 7-5
假定我们发出了一个更新语句:
UPDATE emp SET sal = 4000 Where empno= 7788;
看一下这个语句是怎样执行的(为了简化᧿述,我们尽量简化了情况):
1. 检查 empno=7788 记录在 Buffer Cache 中是否存在,如果不存在则读取到 Buffer
Cache 中
2. 在回滚表空间的相应回滚段事务表上分配事务槽,这个操作需要记录 Redo 信息
3. 从回滚段读入或者在 Buffer Cache 中创建 sal=3000 的前镜像,这需要产生 Redo
信息并记入 Redo Log Buffer
4. 修改 Sal=4000,这是 update 的数据变更,需要记入 Redo Log Buffer。
5. 当用户ᨀ交时,会在 Redo Log Buffer 记录ᨀ交信息,并在回滚段标记该事务为非
激活(inactive)。
让我们通过一个具体的试验来再现这个过程。
1. 首先通过 switch logfile 切换日志
使用 sys 用户进行日志切换,使得接下来的更新可以使用新的日志。
SQL> alter system switch logfile;
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·12·
System altered.
SQL> select * from v$log;
GROUP# THREAD# SEQUENCE# BYTES MEMBERS ARC STATUS FIRST_CHANGE# FIRST_TIM
------ ---------- ---------- ---------- ---------- --- -------- ------------- ---------
1 1 310 10485760 1 NO ACTIVE 8.9035E+12 26-MAR-06
2 1 309 10485760 1 NO INACTIVE 8.9035E+12 19-MAR-06
3 1 311 10485760 1 NO CURRENT 8.9035E+12 26-MAR-06
4 1 308 1048576 1 NO INACTIVE 8.9035E+12 19-MAR-06
2. 更新并ᨀ交事务
SQL> select * from emp where empno=7788;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ------ ------- ------ --------- ---------- ------- ----------
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
SQL> update emp set sal=4000 where empno=7788;
1 row updated.
SQL> commit;
Commit complete.
3. 确认 Session 信息
SQL> select sid,serial#,username from v$session
2 where username='SCOTT';
SID SERIAL# USERNAME
---------- ---------- ------------------------------
13 405 SCOTT
4. 使用 sys 用户在另外 Session 转储日志文件
SQL> ALTER SYSTEM DUMP LOGFILE '/opt/oracle/oradata/conner/redo03.log';
System altered.
SQL> @gettrcname
TRACE_FILE_NAME
------------------------------------------------------------------------------------
/opt/oracle/admin/conner/udump/conner_ora_31885.trc
5. 获取 Trace 文件
从日志文件的转储信息中,我们可以很容易的找到这个事务(sid= 15,serial#=43870)的信
息,为了方便说明,我将这段日志分开讲解:
a)改变向量 1,这是对于回滚段头的修改,分配事务表,从绝对文件号为 2(AFN:2)我
们可以知道这是 UNDO 表空间,通过 UBA 机 DBA 的换算我们能够找到相应的 Block
REDO RECORD - Thread:1 RBA: 0x000137.00000005.0010 LEN: 0x0198 VLD: 0x01
SCN: 0x0819.0036f14d SUBSCN: 1 03/26/2006 12:01:44
CHANGE #1 TYP:0 CLS:19 AFN:2 DBA:0x00800009 SCN:0x0819.0036f03c SEQ: 1 OP:5.2
ktudh redo: slt: 0x001d sqn: 0x000038ea flg: 0x0012 siz: 108 fbi: 0
第 1 章 章名章名章名章名章名
·13·
uba: 0x008000c3.04b1.0c pxid: 0x0000.000.00000000
b)改变向量 2
这里记录的是前镜像信息,注意到,col 5: [ 2] c2 1f 记录的就是对于 COL 5 的修改,
修改前的数值是 3000 (c2 1f,数值及存储转换方式参考本章附录)
CHANGE #2 TYP:0 CLS:20 AFN:2 DBA:0x008000c3 SCN:0x0819.0036f03b SEQ: 1 OP:5.1
ktudb redo: siz: 108 spc: 6740 flg: 0x0012 seq: 0x04b1 rec: 0x0c
xid: 0x0002.01d.000038ea
ktubl redo: slt: 29 rci: 0 opc: 11.1 objn: 7961 objd: 7961 tsn: 0
Undo type: Regular undo Begin trans Last buffer split: No
Temp Object: No
Tablespace Undo: No
0x00000000 prev ctl uba: 0x008000c3.04b1.0b
prev ctl max cmt scn: 0x0819.00364c81 prev tx cmt scn: 0x0819.00365073
KDO undo record:
KTB Redo
op: 0x03 ver: 0x01
op: Z
KDO Op code: URP row dependencies Disabled
xtype: XA bdba: 0x00405c5a hdba: 0x00405c59
itli: 2 ispac: 0 maxfr: 4863
tabn: 0 slot: 7(0x7) flag: 0x2c lock: 0 ckix: 0
ncol: 8 nnew: 1 size: 0
col 5: [ 2] c2 1f
c)改变向量 3
这里记录的是对于数据块的修改,col 5: [ 2] c2 29 记录的是对于 COL 5 的修改,修改
后的值为 4000(c2 29).
CHANGE #3 TYP:2 CLS: 1 AFN:1 DBA:0x00405c5a SCN:0x0819.0036efb1 SEQ: 1 OP:11.5
KTB Redo
op: 0x11 ver: 0x01
op: F xid: 0x0002.01d.000038ea uba: 0x008000c3.04b1.0c
Block cleanout record, scn: 0x0819.0036f14d ver: 0x01 opt: 0x02, entries follow...
itli: 1 flg: 2 scn: 0x0819.0036efb1
KDO Op code: URP row dependencies Disabled
xtype: XA bdba: 0x00405c5a hdba: 0x00405c59
itli: 2 ispac: 0 maxfr: 4863
tabn: 0 slot: 7(0x7) flag: 0x2c lock: 2 ckix: 0
ncol: 8 nnew: 1 size: 0
col 5: [ 2] c2 29
d)改变向量 4
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·14·
当事务ᨀ交之后,记录的 SCN 信息,注意这里标记为“MEDIA RECOVERY MARKER
SCN”,也就是说,这是一个可以恢复的时间点,事务的恢复,必须以 Redo Record 为最小
单位。
CHANGE #4 MEDIA RECOVERY MARKER SCN:0x0000.00000000 SEQ: 0 OP:5.20
e)session 信息
最后部分记录的是产生这些 Redo 的 session 信息。
session number = 13
serial number = 405
transaction name =
从以上的分析中可以看到,对于数据块的修改,如果执行写出,那么通常需要写出 8K 的
Block,而对于 Redo 日志来说,重做信息却相当精简,Oracle 只需要记录那些重构事务必须的
信息(事务号,文件号,块号,行号,字段等)即可,这个数据量大大减少。
3. 操作代码(OP Code)
在以上的改变向量分析中,存在一个重要的操作代码(OP=Operation Code),该代码标志了操
作的类型.下表列出了不同代码代表的操作级别,进一步的,操作级别和具体编号联合就可以详
细的标示一个 Change Vector 的操作:
例如对于 DML 事务,其 Level 是 11,相应的操作代码如下表所示:
第 1 章 章名章名章名章名章名
·15·
对于前面的更新示范,其 OP 代码正是 11.5 . 对于 UNDO 的操作,其代码如下表所示,其中
5.1 表示对 UNDO 块或者 UNDO Header 的操作,5.2 表示对于回滚段头的更新操作:
4. 日志分析获取篡改信息案例一则
在某客户数据库系统中,遇到一则数据被恶意篡改的案例.某用户账户余额为 0 元,被修
改为 40000 元.这一篡改说明数据库存在极大的安全隐患,如果不能及时找和消除这一隐患,
则数据库可能随时处在危险之中,如果遭遇大规模篡改或数据截断,则损失将会极其巨大.
用户遭遇的具体问题为:
login_name=7847254的balance字段数据被恶意修改,客户反映数据被修改的时间段为
7.5-7.6日9:00,balance字段被修改为40000。客户于7.6日上午11:00将此值重置为0.
根据这一信息,我们开始分析日志,进行追踪.
首先获取该行记录的 ROWID 等信息:
SQL> select rowid from "CINMS"."BROAD_SUBSCRB" where login_name='7847254';
ROWID
------------------
AAAQXKAAvAAAB2wAAn
根据 ROWID 可疑找到该记录的块号等信息:
SQL> select get_rowid('AAAQXKAAvAAAB2wAAn') from dual;
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·16·
GET_ROWID('AAAQXKAAVAAAB2WAAN')
--------------------------------------------------------------------------------
Object# is :67018
Relative_fno is :47
Block number is :7600
Row number is :39
get_rowid 是根据 dbms_rowid 包编写的一个函数,用于简化 ROWID 拆分,其代码如下:
create or replace function get_rowid
(l_rowid in varchar2)
return varchar2
is
ls_my_rowid varchar2(200);
rowid_type number;
object_number number;
relative_fno number;
block_number number;
row_number number;
begin
dbms_rowid.rowid_info(l_rowid,rowid_type,object_number,relative_fno, block_number,
row_number);
ls_my_rowid := 'Object# is :'||to_char(object_number)||chr(10)||
'Relative_fno is :'||to_char(relative_fno)||chr(10)||
'Block number is :'||to_char(block_number)||chr(10)||
'Row number is :'||to_char(row_number);
return ls_my_rowid ;
end;
/
我们可疑看到,被修改的数据行位于文件 47 的 Block 号 7600 上.通过 Logminer 解析日
志,可疑找到所有对于这个数据块的修改.
解析日志的步骤如下:
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE(LogFileName => '/ora_arch_backup/201107arch/1_12446.dbf',Options =>
DBMS_LOGMNR.NEW);
PL/SQL procedure successfully completed.
SQL> EXECUTE DBMS_LOGMNR.START_LOGMNR(OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG
+ DBMS_LOGMNR.COMMITTED_DATA_ONLY);
PL/SQL procedure successfully completed.
第 1 章 章名章名章名章名章名
·17·
解析完成之后,可疑通过 v$logmnr_contents 视图来查找相关的修改操作.在该日志中,
解析后共得到约 18 万行数据:
SQL> select count(*) from v$logmnr_contents;
COUNT(*)
----------
182001
对数据块 7600 的修改只有一次:
SQL> select count(*) from v$logmnr_contents where DATA_BLK#=7600;
COUNT(*)
----------
1
该修改正是位于 BROAD_SUBSCRB 表,但是 SQL_REDO 和 SQL_UNDO 信息不可用:
SQL> select scn,seg_name,sql_redo,sql_undo,rs_id from v$logmnr_contents where scn=5568491262;
SCN SEG_NAME SQL_REDO SQL_UNDO RS_ID
---------- --------------- ------------ ------------ -------------------------
5568491262 BROAD_SUBSCRB Unsupported Unsupported 0x00309e.00028a0a.0010
SQL> select scn,session#,sql_redo,sql_undo,rs_id from v$logmnr_contents where
DATA_BLK#=7600;
SCN SESSION# SQL_REDO SQL_UNDO RS_ID
---------- ---------- ------------ ------------ ------------------------------
5568491262 90 Unsupported Unsupported 0x00309e.00028a0a.0010
根据 session#,可以将这个会话的所有操作查询出来,注意第一条信息的绝对文件号是 2,也
就是 UNDO 表空间,说明该条目记录的是事务相关信息;第二条信息记录的是对于绝对文件号
47 块号 7602 号的操作:
SQL> select ABS_FILE#,REL_FILE#,DATA_BLK#,DATA_OBJ#,SEG_NAME ,rs_id from v$logmnr_contents
where session#=90 and seg_name='BROAD_SUBSCRB';
ABS_FILE# REL_FILE# DATA_BLK# DATA_OBJ# SEG_NAME RS_ID
---------- ---------- ---------- ---------- --------------- ------------------------------
2 47 7600 66237 BROAD_SUBSCRB 0x00309e.00028a0a.0010
47 47 7602 66237 BROAD_SUBSCRB 0x00309e.00028b4d.0010
SQL> select TIMESTAMP,ABS_FILE#,REL_FILE#,DATA_BLK#,DATA_OBJ#,sql_redo from
v$logmnr_contents where session#=90 and seg_name='BROAD_SUBSCRB';
TIMESTAMP ABS_FILE# REL_FILE# DATA_BLK# DATA_OBJ# SQL_REDO
------------------- ---------- ---------- ---------- ---------- -------------------------
2011-07-05 16:41:38 2 47 7600 66237 Unsupported
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·18·
2011-07-05 16:41:54 47 47 7602 66237 Unsupported
根据前面查询到的 scn 等信息,可以再次搜索,获取用户的连接信息等,根据这些连接信息,
已经可以确定连接的客户端:
SQL> select session_info from v$logmnr_contents where scn=5568491262;
SESSION_INFO
-----------------------------------------------------------------------------------------
-------------------------------------------
login_username=CINMS client_info= OS_username=Administrator Machine_name=WORKGROUP\ANY
login_username=CINMS client_info= OS_username=Administrator Machine_name=WORKGROUP\ANY
但是我们还需要进一步确认,该次操作是否就是那一次恶意的修改.
我们可以将日志文件转储出来,然后根据查询到的 RS_ID(RS_ID 记录的是 REDO Record
SET的ID号,可以识别 REDO 中的 REDO 记录条目,实际上也就是 RBA 信息)去搜索,找到相关的
UNDO 信息,转出日志可以用类似如下命令:
alter system dump logfile '/ora_backup/arch/1_12606.dbf'
根据前面的查询相关的事务和修改信息的 RS_ID 分别为: 0x00309e.00028a0a.0010 和
0x00309e.00028b4d.0010.
在跟踪文件中可以找到如下第一个 RS_ID 详细的 REDO 信息,这是事务开始之前,对于
UNDO 的修改和锁定:
REDO RECORD - Thread:1 RBA: 0x00309e.00028a0a.0010 LEN: 0x01a4 VLD: 0x01
>>>>这里的 RBA 信息
SCN: 0x0001.4be86efe SUBSCN: 1 07/05/2011 16:41:38
CHANGE #1 TYP:0 CLS:21 AFN:2 DBA:0x00800029 SCN:0x0001.4be86ed9 SEQ: 1 OP:5.2
>>>> OP:5.2 这是更新 UNDO 段 Header,创建一个事务
ktudh redo: slt: 0x0022 sqn: 0x0019482c flg: 0x0012 siz: 108 fbi: 0
uba: 0x00801542.af17.04 pxid: 0x0000.000.00000000
CHANGE #2 TYP:0 CLS:22 AFN:2 DBA:0x00801542 SCN:0x0001.4be86ed8 SEQ: 1 OP:5.1
>>>>OP:5.1 这是 UNDO BLOCK 和 UNDO HEADER 的修改,
ktudb redo: siz: 108 spc: 7640 flg: 0x0012 seq: 0xaf17 rec: 0x04
xid: 0x0003.022.0019482c
ktubl redo: slt: 34 rci: 0 opc: 11.1 objn: 66237 objd: 67018 tsn: 8
Undo type: Regular undo Begin trans Last buffer split: No
Temp Object: No
Tablespace Undo: No
0x00000000 prev ctl uba: 0x00801542.af17.03
prev ctl max cmt scn: 0x0001.4be86985 prev tx cmt scn: 0x0001.4be869b3
KDO undo record:
KTB Redo
第 1 章 章名章名章名章名章名
·19·
op: 0x04 ver: 0x01
op: L itl: xid: 0x0005.023.0019a945 uba: 0x00800c54.b1a9.0b
flg: C--- lkc: 0 scn: 0x0001.4be11d6c
KDO Op code: LKR row dependencies Disabled
xtype: XA bdba: 0x0bc01db0 hdba: 0x0900e509
itli: 2 ispac: 0 maxfr: 4863
tabn: 0 slot: 39 to: 0
CHANGE #3 TYP:2 CLS: 1 AFN:47 DBA:0x0bc01db0 SCN:0x0001.4be21203 SEQ: 1 OP:11.4
>>>>OP:11.4 是锁定行,实际上也就是锁定 ITL
KTB Redo
op: 0x01 ver: 0x01
op: F xid: 0x0003.022.0019482c uba: 0x00801542.af17.04
KDO Op code: LKR row dependencies Disabled
xtype: XA bdba: 0x0bc01db0 hdba: 0x0900e509
itli: 2 ispac: 0 maxfr: 4863
tabn: 0 slot: 39 to: 2
CHANGE #4 MEDIA RECOVERY MARKER SCN:0x0000.00000000 SEQ: 0 OP:5.19
session number = 90
serial number = 14480
current username = CINMS
login username = CINMS
client info =
OS username = Administrator
Machine name = WORKGROUP\ANY
OS terminal = ANY
OS process id = 680:2868
OS program name =
transaction name =
接下来找到第二个 RS_ID 的详细信息,这里就是真正的用户数据更新信息:
REDO RECORD - Thread:1 RBA: 0x00309e.00028b4d.0010 LEN: 0x0114 VLD: 0x01
>>>>RBA 信息,也即 RS_ID
SCN: 0x0001.4be86fb9 SUBSCN: 1 07/05/2011 16:41:54
CHANGE #1 TYP:0 CLS:22 AFN:2 DBA:0x00801542 SCN:0x0001.4be86efe SEQ: 1 OP:5.1
>>>>OP:5.1 这里记录的是前镜像数据
ktudb redo: siz: 116 spc: 7530 flg: 0x0022 seq: 0xaf17 rec: 0x05
xid: 0x0003.022.0019482c
ktubu redo: slt: 34 rci: 4 opc: 11.1 objn: 66237 objd: 67018 tsn: 8
Undo type: Regular undo Undo type: Last buffer split: No
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·20·
Tablespace Undo: No
0x00000000
KDO undo record:
KTB Redo
op: 0x04 ver: 0x01
op: L itl: xid: 0x000a.01e.001a0c96 uba: 0x00800c0a.b193.29
flg: C--- lkc: 0 scn: 0x0001.4bb7744a
KDO Op code: URP row dependencies Disabled
xtype: XA bdba: 0x0bc01db2 hdba: 0x0900e509
itli: 3 ispac: 0 maxfr: 4863
tabn: 0 slot: 47(0x2f) flag: 0x0c lock: 0 ckix: 0
ncol: 33 nnew: 2 size: -1
col 7: [ 1] 35
col 15: [ 1] 80
>>>>这里记录了修改前的值,分别修改了第 7 和第 15 列信息(由 0 编号,等于第 8 和 16 列)
>>>>80 就是十进制的 0
CHANGE #2 TYP:2 CLS: 1 AFN:47 DBA:0x0bc01db2 SCN:0x0001.4be1efdb SEQ: 1 OP:11.5
>>>>OP:11.5 指更新行记录信息
KTB Redo
op: 0x01 ver: 0x01
op: F xid: 0x0003.022.0019482c uba: 0x00801542.af17.05
KDO Op code: URP row dependencies Disabled
xtype: XA bdba: 0x0bc01db2 hdba: 0x0900e509
itli: 3 ispac: 0 maxfr: 4863
tabn: 0 slot: 47(0x2f) flag: 0x0c lock: 3 ckix: 0
ncol: 33 nnew: 2 size: 1
col 7: [ 1] 31
col 15: [ 2] c3 05
>>>>这里是更新后的值,c3 05 就是 40000
最后整理确认时间:
SQL> select TIMESTAMP,ABS_FILE#,REL_FILE#,DATA_BLK#,DATA_OBJ#,rs_id
from v$logmnr_contents where session#=90 and seg_name='BROAD_SUBSCRB';
TIMESTAMP ABS_FILE# REL_FILE# DATA_BLK# DATA_OBJ# RS_ID
------------------- ---------- ---------- ---------- ----------
------------------------------
2011-07-05 16:41:38 2 47 7600 66237 0x00309e.00028a0a.0010
2011-07-05 16:41:54 47 47 7602 66237 0x00309e.00028b4d.0010
通过对监听日志的详细分析,发现了机器名为 ANY 的机器于 7 月 5 日下午登陆服务器的
第 1 章 章名章名章名章名章名
·21·
记录,整个过程被彻底解析:
05-JUL-2011 16:41:22 * (CONNECT_DATA=(SID=cinms)(CID=(PROGRAM=C:\Program
Files\PLSQL Developer\plsqldev.exe)(HOST=ANY)(USER=Administrator))) *
(ADDRESS=(PROTOCOL=tcp)(HOST=100.40.0.165)(PORT=1940)) * establish * cinms * 0
剩下的就是要看如何去和相关人员交流沟通了.
作为一个数据库从业人员,我们需要从中了解到:接触数据,意味着责任与义务,必须具备
严格的职业道德操守,做数据的保护人而不是威胁者.
7.7产生多少 Redo?
我们知道,对于数据库的修改操作都会记录 redo,那么不同的操作会产生多少 Redo 呢?
可以通过以下一些方式来查询。
1. 在 SQL*plus 中使用 autotrace 的功能时
当我们在 SQL*plus 中启用 autotrace 跟踪后,在执行了特定的 DML 语句时,Oracle 会显
示该语句的统计信息,其中,redo size 一栏表示的就是该操作产生的 Redo 的数量,其单位为
Bytes:
SQL> set autotrace trace stat
SQL> insert into eygle
2 select * from eygle;
28 rows created.
Statistics
----------------------------------------------------------
4 consistent gets
0 physical reads
776 redo size
2. 通过 v$mystat 查询
Oracle 通过 v$mystat 视图记录当前 Session 的统计信息,我们也可以从该视图中查询得到
Session 的 Redo 生成情况:
SQL> col name for a30
SQL> select a.name,b.value
2 from v$statname a,v$mystat b
3 where a.STATISTIC# = b.STATISTIC# and a.name = 'redo size';
NAME VALUE
------------------------ ----------
redo size 56540
SQL> insert into eygle select * from eygle;
56 rows created.
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·22·
SQL> select a.name,b.value
2 from v$statname a,v$mystat b
3 where a.STATISTIC# = b.STATISTIC# and a.name = 'redo size';
NAME VALUE
------------------------------ ----------
redo size 57784
SQL> select 57784 -56540 from dual;
57784-56540
-----------
1244
3. 通过 v$sysstat 查询
对于数据库全局 Redo 的生成量,我们可以通过 v$sysstat 视图来查询得到:
SQL> col value for 99999999999999
SQL> select name,value
2 from v$sysstat where name='redo size';
NAME VALUE
------------------------------ ---------------
redo size 2065603825384
从 v$sysstat 视图中得到的是自数据库实例启动以来的累积日志生成量,我们可以根据实
例启动时间来大致估算每天数据库的日志生成量:
SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
Session altered.
SQL> select startup_time from v$instance;
STARTUP_TIME
-------------------
2005-08-25 03:01:04
SQL> select (select value/1024/1024/1024 from v$sysstat where name='redo size')/
2 (select round(sysdate - ( select startup_time from v$instance)) from dual)
REDO_GB_PER_DAY
3 from dual;
REDO_GB_PER_DAY
---------------
7.42880595
如果数据库运行在归档模式下,由于其他因素的影响,以上 Redo 生成量并不代表归档日
志的大小,但是可以通过一定的加权ᨀ供参考。
至于归档日志的生成量,可以通过 v$archived_log 视图,根据一段时间的归档日志量进行
估算得到。该视图中记录了归档日志的主要信息:
SQL> select name,COMPLETION_TIME,BLOCKS*BLOCK_SIZE/1024/1024 Mb
第 1 章 章名章名章名章名章名
·23·
from v$archived_log where rownum <11
and COMPLETION_TIME between trunc(sysdate) -2 and trunc(sysdate) -1;
NAME COMPLETION_TIME MB
---------------------------------------- ------------------- ----------
/bsarch/oracle/1_171913.dbf 2006-05-09 00:00:09 19.996582
/bsarch/oracle/1_171914.dbf 2006-05-09 01:01:13 19.9990234
/bsarch/oracle/1_171915.dbf 2006-05-09 01:35:57 19.9931641
/bsarch/oracle/1_171917.dbf 2006-05-09 02:02:46 19.9990234
/bsarch/oracle/1_171917.dbf 2006-05-09 02:06:23 19.9990234
/bsarch/oracle/1_171918.dbf 2006-05-09 02:09:56 19.9990234
/bsarch/oracle/1_171919.dbf 2006-05-09 02:13:36 19.9990234
/bsarch/oracle/1_171920.dbf 2006-05-09 02:16:21 19.9990234
/bsarch/oracle/1_171921.dbf 2006-05-09 03:00:28 19.9990234
/bsarch/oracle/1_171922.dbf 2006-05-09 03:01:03 4.03271484
10 rows selected.
某日全天的日志生成可以通过如下查询计算:
SQL> select trunc(COMPLETION_TIME),sum(Mb)/1024 DAY_GB from
2 (select name,COMPLETION_TIME,BLOCKS*BLOCK_SIZE/1024/1024 Mb from v$archived_log
3 where COMPLETION_TIME between trunc(sysdate) -2 and trunc(sysdate) -1)
4 group by trunc(COMPLETION_TIME)
5 /
TRUNC(COM DAY_GB
--------- ----------
09-MAY-06 17.8974366
最近日期的日志生成统计:
SQL> SELECT TRUNC (completion_time), SUM (mb) / 1024 day_gb
2 FROM (SELECT NAME, completion_time, blocks * block_size / 1024 / 1024 mb
3 FROM v$archived_log)
4 GROUP BY TRUNC (completion_time);
TRUNC(COM DAY_GB
--------- ----------
28-APR-06 8.63226318
29-APR-06 11.6235332
30-APR-06 17.7366991
01-MAY-06 35.7830167
02-MAY-06 11.0832992
03-MAY-06 11.6479049
04-MAY-06 8.76808453
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·24·
05-MAY-06 9.68909311
06-MAY-06 14.186295
07-MAY-06 10.4164033
08-MAY-06 19.9013429
09-MAY-06 17.8974366
10-MAY-06 19.4107008
11-MAY-06 11.2606988
14 rows selected.
根据每日归档的生成量,我们也可以反过来估计每日的数据库活动性及周期性,并决定
空间分配等问题。将这些数据导入 Excel,可以很容易的获得直观的曲线图,进行辅助分析与
报告(以下图表来自不同数据):
7.8redo 写的触发条件
为了保证用户可以快速ᨀ交,LGWR 的写出必须非常活跃,实际上也确实如此,我们非
常熟悉的 LGWR 写触发条件就有:
7.8.1 每 3 秒钟超时(Timeout)
当 LGWR 处于空闲状态时,它依赖于 rdbms ipc message 等待,处于休眠状态,直到 3 秒
超时时间到。如果 LGWR 发现有 redo 需要写出,那么 LGWR 将执行写出操作,log file parallel
第 1 章 章名章名章名章名章名
·25·
write 等待事件将会出现。
启用 10046 事件,从 LGWR 跟踪日志中可以清楚的观察到这些事件:
WAIT #0: nam='rdbms ipc message' ela= 2999554 p1=300 p2=0 p3=0
WAIT #0: nam='rdbms ipc message' ela= 2999470 p1=300 p2=0 p3=0
WAIT #0: nam='rdbms ipc message' ela= 566819 p1=300 p2=0 p3=0
WAIT #0: nam='log file parallel write' ela= 115 p1=1 p2=2 p3=1
WAIT #0: nam='rdbms ipc message' ela= 45752 p1=213 p2=0 p3=0
WAIT #0: nam='log file parallel write' ela= 94 p1=1 p2=3 p3=1
WAIT #0: nam='rdbms ipc message' ela= 51762 p1=208 p2=0 p3=0
WAIT #0: nam='log file parallel write' ela= 91 p1=1 p2=1 p3=1
WAIT #0: nam='rdbms ipc message' ela= 29033 p1=200 p2=0 p3=0
WAIT #0: nam='log file parallel write' ela= 99 p1=1 p2=2 p3=1
WAIT #0: nam='rdbms ipc message' ela= 40293 p1=197 p2=0 p3=0
WAIT #0: nam='log file parallel write' ela= 87 p1=1 p2=1 p3=1
7.8.2 阈值达到
我们在各种文档上经常会看到的 2 个触发日志写的条件是:
Redo Log Buffer 1/3 满
Redo Log Buffer 具有 1M 脏数据
这两者都是限制条件,在触发时是协同生效的。
我们知道,只要有进程(Process)在 log buffer 中分配和使用空间,已经使用的 Log buffer
的数量将被计算。如果使用的块的数量大于或等于一个隐含参数_log_io_size 的设置,那么将
会触发 LGWR 写操作。
如果此时 LGWR 未处于活动状态,那么 LGWR 将被通知去执行后台写操作。
缺省的_log_io_size 等于 1/3 log buffer 大小,上限值为 1M,此参数在 X$KSPPSV 中显示
的 0 值,意为缺省值。
X$KSPPSV 的含义为: [K]ernel [S]ervice [P]arameter Component [S]ystem [V]alues
也就是,LGWR 将在 Min(1M,1/3 log buffer size)时触发。注意此处的 log buffer size 是以
log block 来衡量的。
SQL> @D:\GetHiddenParameter.sql
Enter value for par: log_io
old 14: x.ksppinm like '%_&par%'
new 14: x.ksppinm like '%_log_io%'
NAME VALUE ISDEFAULT ISMOD ISADJ
------------------------------ ------------------------- --------- ---------- -----
_log_io_size 0 TRUE FALSE FALSE
一个常见的经验推荐是将 Log Buffer 设置为 3M 大小,就是因为当 Redo Log Buffer 为 3M
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·26·
时,以上 2 个条件可能同时达到,从而可以在某种程度上避免 LGWR 的过度激活。
7.8.3 用户ᨀ交
当一个事物ᨀ交时,在 redo stream 中将记录一个ᨀ交标志。
在这些 redo 被写到磁盘上之前,这个事物是不可恢复的。所以,在事务返回成功标志给
用户前,必须等待 LGWR 写完成。进程通知 LGWR 写,并且以 log file sync 事件开始休眠,
超时时间为 1 秒。
Oracle 的隐含参数_wait_for_sync 参数可以设置为 false 避免 redo file sync 的等待,但是就
将无法保证事务的恢复性。
SQL> @D:\GetHiddenParameter.sql
Enter value for par: wait_for
NAME VALUE ISDEFAULT ISMOD ISADJ
------------------ -------------------- ----- --------- -----
_wait_for_sync TRUE TRUE FALSE FALSE
注意,在递归调用(recursive calls)中的ᨀ交(比如过程中的ᨀ交)不需要同步 redo 直到需要返
回响应给用户。因此递归调用仅需要同步返回给用户调用之前的最后一次 Commit 操作的 RBA。
存在一个 SGA 变量用以记录 redo 线程需要同步的 log block number。如果多个ᨀ交在唤
醒 LGWR 之前发生,此变量记录最高的 log block number,在此之前的所有 redo 都将被写入
磁盘。这有时候被称为组ᨀ交(group commit).
7.8.4 在 DBWn 写之前
如果 DBWR 将要写出的数据的 High RBA 超过 LGWR 的 On-Disk RBA,DBWR 将通知
LGWR 去执行写出(否则这部分数据在 Recovery 时将无法恢复)。在 Oracle8i 之前,此时 DBWR
将等待 log file sync 事件;从 Oracle8i 开始,DBWR 把这些 Block 放入一个延迟队列,同时通
知 LGWR 执行 redo 写出,DBWR 可以继续执行无需等待的数据写出。
在生产环境中,通常用户ᨀ交的频率是很高的,下面是来自 Statspack 的一段报告:
Statistic Total per Second per Trans
--------------------------------- ------------------ --------------
.....
redo blocks written 432,214 238.4 10.2
redo buffer allocation retries 4 0.0 0.0
redo entries 224,270 123.7 5.3
redo log space requests 4 0.0 0.0
redo log space wait time 8 0.0 0.0
redo size 207,176,400 114,272.7 4,905.3
redo synch time 573,356 317.3 13.6
redo synch writes 45,230 25.0 1.1
redo wastage 7,261,484 4,005.2 171.9
第 1 章 章名章名章名章名章名
·27·
redo write time 145,896 80.5 3.5
redo writer latching time 37 0.0 0.0
redo writes 29,608 17.3 0.7
.....
user calls 876,983 483.7 20.8
user commits 42,235 23.3 1.0
.............
注意到这个数据库中,平均每秒用户就ᨀ交了 23.3 次。
7.9Redo Log Buffer 的大小设置
Redo Log Buffer 的大小由初始化参数 LOG_BUFFER 定义。
该参数的缺省值:Max(512 KB , 128 KB * CPU_COUNT)
通常这一缺省值是足够的,从上一节我们可以知道,Redo Log Buffer 的写出操作是相当
频繁的,所以过大的 Log Buffer 设置通常是没有必要的;如果缺省值不能满足要求,根据我们
前面的介绍,一般来说 3M 是一个较为合理的调整开端。
Log_buffer 参数的设置是否需要调整,可以从数据库的等待事件来判断:
SQL> select event#,name from v$event_name where name='log buffer space';
EVENT# NAME
---------- ----------------------------------------------------------------
196 log buffer space
当 Log Buffer Space 等待事件出现并且较为显著时,我们可以考虑增大 Log Buffer 以缩减
竞争。
从 Oracle10g 开始,LOG_BUFFER 的分配算法有所改变,由于 Granule 的引入,
LOG_BUFFER 的分配也需要以 Granule 为基础,缺省的 SGA 中的 Fixed SGA Size 将和
LOG_BUFFER 共享整数倍的 Granule:
SQL> select * from v$version where rownum <2;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
SQL> show parameter log_buffer
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_buffer integer 7053312
SQL> select * from v$sgainfo
2 where name in ('Fixed SGA Size','Redo Buffers','Granule Size');
NAME BYTES RES
------------------------------ ---------- ---
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
·28·
Fixed SGA Size 1223488 No
Redo Buffers 7163904 No
Granule Size 4194304 No
SQL> select (1223488 +7163904)/4194304 from dual;
(1223488+7163904)/4194304
-------------------------
1.99971008
如果不是有明显的性能问题,一般缺省的设置是足够的
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
Oracle DataGuard高可用性解决方案详解
孙莹
590次阅读
2025-03-26 23:27:33
Oracle RAC 一键安装翻车?手把手教你如何排错!
Lucifer三思而后行
544次阅读
2025-04-15 17:24:06
【纯干货】Oracle 19C RU 19.27 发布,如何快速升级和安装?
Lucifer三思而后行
461次阅读
2025-04-18 14:18:38
Oracle SQL 执行计划分析与优化指南
Digital Observer
442次阅读
2025-04-01 11:08:44
XTTS跨版本迁移升级方案(11g to 19c RAC for Linux)
zwtian
440次阅读
2025-04-08 09:12:48
【ORACLE】记录一些ORACLE的merge into语句的BUG
DarkAthena
437次阅读
2025-04-22 00:20:37
墨天轮个人数说知识点合集
JiekeXu
436次阅读
2025-04-01 15:56:03
【ORACLE】你以为的真的是你以为的么?--ORA-38104: Columns referenced in the ON Clause cannot be updated
DarkAthena
412次阅读
2025-04-22 00:13:51
Oracle数据库一键巡检并生成HTML结果,免费脚本速来下载!
陈举超
404次阅读
2025-04-20 10:07:02
Oracle 19c RAC更换IP实战,运维必看!
szrsu
392次阅读
2025-04-08 23:57:08