2012年4月13日星期五

理解REDO LOG-3(转)


LOG BUFFER 和LGWR

   REDO LOG的产生十分频繁,几乎每秒钟都有几百K到几M的RED LOG产生,甚至某些大型数据库每秒钟产生的REDO LOG量达到了10M以上。不过前台进程每次产生的REDO量却不大,一般在几百字节到几K,而一般来所一个事务产生的REDO 量也不过几K到几十K。基于REDO产生的这个特点,如果每次REDO产生后就必须写入REDO LOG文件,那么就会存在两个问题,一个是REDO LOG文件写入的频率过高,会导致REDO LOG文件的IO存在问题,第二个是如果由前台进程来完成REDO LOG的写入,那么会导致大量并发的前台进程产生REDO LOG文件的争用。为了解决这两个问题,Oracle在REDO LOG机制中引入了LGWR后台进程和LOG BUFFER。

   LOG BUFFER是Oracle用来缓存前台进程产生的REDO LOG信息的,有了LOG BUFFER,前台进程就可以将产生的REDO LOG信息写入LOG BUFFER,而不需要直接写入REDO LOG文件,这样就大大提高了REDO LOG产生和保存的时间,从而提高数据库在高并发情况下的性能。

   既然前台进程不将REDO LOG信息写入REDO LOG文件了,那么就必须要有一个后台进程来完成这个工作。这个后台进程就是LGWR,LGWR进程的主要工作就是将LOG BUFFER中的数据批量写入到REDO LOG文件中。对于Oracle数据库中,只要对数据库的改变写入到REDO LOG文件中了,那么就可以确保相关的事务不会丢失了。

   引入LOG BUFFER后,提高了整个数据库RDMBS写日志的性能,但是如何确保一个已经提交的事务确确实实的被保存在数据库中,不会因为之后数据库发生故障而丢失呢?实际上在前面两节中我们介绍的REDO LOG的一些基本的算法确保了这一点。首先WRITE AHEAD LOG协议确保了只要保存到REDO LOG文件中的数据库变化一定能够被重演,不会丢失,也不会产生二义性。其次是在事务提交的时候,会产生一个COMMIT的CV,这个CV被写入LOG BUFFER后,前台进程会发出一个信号,要求LGWR将和这个事务相关的REDO LOG信息写入到REDO LOG文件中,只有这个事务相关的REDO LOG信息已经确确实实被写入REDO LOG文件的时候,前台进程才会向客户端发出事务提交成功的消息,这样一个事务才算是被提交完成了。在这个协议下,只要客户端收到了提交完成的消息,那么可以确保,该事务已经存盘,不会丢失了。LGWR会绕过操作系统的缓冲,直接写入数据文件中,以确保REDO LOG的信息不会因为操作系统出现故障(比如宕机)而丢失要求确保写入REDO LOG文件的数据。

   实际上,虽然Oracle数据库使用了绕过缓冲直接写REDO LOG文件的方法,以避免操作系统故障导致的数据丢失,不过我们还是无法确保这些数据已经确确实实被写到了物理磁盘上。因为我们RDBMS使用的绝大多数存储系统都是带有写缓冲的,写缓冲可以有效的提高存储系统写性能,不过也带来了另外的一个问题,就是说一旦存储出现故障,可能会导致REDO LOG的信息丢失,甚至导致REDO LOG出现严重损坏。存储故障的概率较小,不过这种小概率事件一旦发生还是会导致一些数据库事务的丢失,因此虽然Oracle的内部算法可以确保一旦事务提交成功,事务就确认被保存完毕了,不过还是可能出现提交成功的事务丢失的现象。

   实际上,Oracle在设计REDO LOG文件的时候,已经最大限度的考虑了REDO LOG文件的安全性,REDO LOG文件的BLOCK SIZE和数据库的BLOCK SIZE是完全不同的,REDO LOG文件的BLOCK SIZE是和操作系统的IO BLOCK SZIE完全相同的,这种设计确保了一个REDO LOG BLOCK是在一次物理IO中同时写入的,因此REDO LOG BLOCK不会出现块断裂的现象。

   了解LOG BUFFER和LGWR的算法,有助于我们分析和解决相关的性能问题,因此我们需要花一点时间来了解LOG BUFFER相关的基本算法。用一句话来概括,LOG BUFFER是一个循环使用的顺序型BUFFER。这句话里包含了两个含义,一个是LOG BUFFER是一个顺序读写的BUFFER,LOG BUFFER数据的写入是顺序的;第二个含义是LOG BUFFER是一个循环BUFFER,当LOG BUFFER写满后,会回到头上来继续写入REDO LOG信息。LOG BUFFER数据的写入是由前台进程完成的,这个写入操作是并发的,每个前台进程在生成了REDO LOG信息后,需要首先在LOG BUFFER中分配空间,然后将REDO LOG信息写入到LOG BUFFER中去。在LOG BUFFER中分配空间是一个串行的操作,因此Oracle在设计这方面的算法的时候,把LOG BUFFER空间分配和将REDO LOG数据拷贝到LOG BUFFER中这两个操作分离了,一旦分配了LOG BUFFER空间,就可以释放相关的闩锁,其他前台进程就可以继续分配空间了(这里所说的前台进程只是一个泛指,是为了表述方便而已,读者一定要注意,因为后台进程也会对数据库进行修改,也需要产生REDO LOG信息,后台进程的REDO 操作和前台进程是大体一致的)。

   前台进程写入REDO 信息会使LOG BUFFER的尾部指针不停的向前推进,而LGWR这个后台进程不听的从LOG BUFFER的头部指针处开始查找还未写入REDO LOG文件的LOG BUFFER信息,并将这些信息写入REDO LOG文件中,并且将BUFFER头部指针不停的向后推进,一旦LOG BUFFER的头部指针和尾部指针重合,那么就说嘛了当前的LOG BUFFER是空的。而如果前台进程在LOG BUFFER中分配空间会使LOG BUFFER的尾部指针一直向前推进,一旦LOG BUFFER的尾部指针追上了LOG BUFFER的头部指针,那么说明目前LOG BUFFER中无法分配新的空间给后台进程了,后台进程必须要等候LGWR将这些数据写入REDO LOG文件,然后向前推进了头部指针,才可能再次获得新的可用BUFFER空间。这个时候,前台进程会等待LOG FILE SYNC事件。

为了让LGWR尽快将LOG BUFFER中的数据写入REDO LOG文件,以便于腾出更多的空闲空间,Oracle数据库设计了LGWR写的触发条件:

l 事务提交时

l LOG BUFFER中的数据超过1M

l 当LOG BUFFER中的数据超过了_log_io_size隐含参数指定的大小

l 每隔3秒钟

   前面我们多次提到了,当事务提交时,会产生一个提交的REDO RECORD,这个RECORD写入LOG BUFFER后,前台进程会触发LGWR写操作,这个时候前台进程就会等待LOG FILE SYNC等待,直到LGWR将相关的数据写入REDO LOG文件,这个等待就会结束,前台进程就会收到提交成功的消息。如果我们的系统中,每秒的事务数量较大,比如几十个或者几百个,甚至大型OLTP系统可能会达到每秒数千个事务。在这种系统中,LGWR由于事务提交而被激发的频率很高,LOG BUFFER的信息会被很快的写入REDO LOG文件中。

   而对于某些系统来说,平均每个事务的大小很大,平均每个事务生成的REDO LOG数据量也很大,比如1M甚至更高,平均每秒钟的事务数很少,比如1-2个甚至小于一个,那么这种系统中LGWR由于事务提交而被激发的频率很低,可能导致REDO LOG信息在LOG BUFFER中被大量积压,oracle设计的LOG BUFFER中数据超过1M的LGWR激发条件就是为了解决这种情况而设计的,当LOG BUFFER中的积压数据很多时,虽然没有事务提交,也会触发LGWR将BUFFER中的数据写入REDO LOG文件。

   除此之外,Oracle还通过了_LOG_IO_SIZE这个隐含参数来进一步控制LGWR写操作,当LOG BUFFER中的数据超过了这个隐含参数的规定的大小,也会触发LGWR被激发。这个参数的缺省值是LOG BUFFER大小的1/3,这个参数单位是REDO LOG BLOCK。这个参数可以控制当LOG BUFFER中有多少个数据块被占用时,就要触发LGWR写操作,从而避免LOG BUFFER被用尽。

如果一个系统很空闲,很长时间都没有事务提交,LOG BUFFER的使用也很少,就可能会导致LOG BUFFER中的数据长期没有被写入REDO LOG文件,带来丢失数据的风险,因此Oracle还设计了一个LGWR写的激发条件,设置了一个时间触发器,每隔3秒钟,这个触发器都会被激活,这个触发器被激活的时候,如果发现LOG BUFFER不是空的,并且LGWR不处于活跃状态,就会产生一个事件,激活LGWR。

   前面我们讨论了LGWR和LOG BUFFER的一些基本的算法,那么下面我们来讨论讨论LOG FILE SYNC等待事件。LOG FILE SYNC等待的含义是等待LGWR将LOG BUFFER的数据写入到REDO LOG文件中。一般情况下,如果某个事务在做提交的时候,会等待LOG FILE SYNC,而没有做提交操作的会话不需要等待LOG FILE SYNC,因为前台进程只需要将REDO LOG信息写入到LOG BUFFER中就可以了,不需要等待这些数据被写入REDO LOG文件。不过如果前台进程在分配LOG BUFFER的时候,如果发现LOG BUFFER的尾部已经追上了LOG BUFFER的头部,那么前台进程就要等待LGWR进程将头部的数据写入REDO LOG文件,然后释放LOG BUFFER空间。这个时候,没有做提交操作的前台进程都会等待LOG FILE SYNC事件。这种情况下,加大LOG BUFFER就可能可以减少大部分的LOG FILE SYNC等待了。

   加大LOG BUFFER的大小,可能会带来另外一个问题,比如LOG BUFFER从1M增加到30M(关于LOG BUFFER是否需要大于3M的问题,以前我们已经多次讨论,因此在这里不再讨论了,大家只需要记住一点就可以了,LOG BUFFER大于3M浪费空间,对性能影响不大的观点是错误的),那么_LOG_IO_SIZE自动会从300K增加到10M,在一个平均每秒事务数较少,并且每个事务的REDO SIZE较大的系统中,触发LGWR写操作的LOG BUFFER数据量会达到1M。一般来说,在一个大型的OLTP系统里,每次LGWR写入REDO LOG文件的大小在几K到几十K之间,平均LOG FILE SYNC的时间在1-10毫秒之间。如果平均每次写入的数据量过大,会导致LOG FILE SYNC的等待时间变长。因此在这种情况下,就可能需要设置_LOG_IO_SIZE参数,确保LOG FILE SYNC等待不要过长。

   如果每次写入REDO LOG文件的数据量也不大,而LOG FILE SYNC等待时间很吵,比如说超过100毫秒,那么我们就要分析一下REDO LOG文件的IO性能了,如果REDO LOG文件IO性能不佳,或者该文件所在的IO热点较大,也可能导致LOG FILE SYNC等待时间偏大,这种情况,我们可以查看后台进程的LOG FILE PARALLEL WRITE这个等待事件,这个等待事件一般的等待时间为几个毫秒,如果这个等待事件的平均等待时间较长,那么说明REDO LOG文件的IO性能不佳,需要将REDO LOG文件放到IO量较小,性能较快的磁盘上。

   在OLTP系统上,REDO LOG文件的写操作主要是小型的,比较频繁,一般的写大小在几K,而每秒钟产生的写IO次数会达到几十次,数百次甚至上千次。因此REDO LOG文件适合存放于IOPS较高的转速较快的磁盘上,IOPS仅能达到数百次的SATA盘不适合存放REDO LOG文件。另外由于REDO LOG文件的写入是串行的,因此对于REDO LOG文件所做的底层条带化处理,对于REDO LOG写性能的提升是十分有限的。

理解REDO LOG-2(转)


变化矢量和REDO RECORD
今天我们要了解一些REDO LOG底层的概念,只有将这几个概念搞明白了,我们才能更加深入的了解REDO LOG,以及了解REDO LOG相关的管理和优化的要点。首先我们要了解的就是变化矢量(CHANGE VECTORCV),变化矢量是组成REDO信息的基础,一个变化矢量描述了对一个独立的数据块的一个独立的修改操作。这里面我们要注意的是,对于CV的定义里包含了两层含义,一个CV只针对一个数据块的变更,一个CV只包含一个变化。每个CV都包含了对文件的修改,因此在每个CV中都有一个OPCODE,指出修改的类型。不同OPCODECV,其组成是不同的,OPCODE的取值范围包括:
    Layer 1 : Transaction Control - KCOCOTCT     
Opcode 1 : KTZFMT 
Opcode 2 : KTZRDH 
Opcode 3 : KTZARC
Opcode 4 : KTZREP
     
    Layer 2 : Transaction Read -  KCOCOTRD     
     
    Layer 3 : Transaction Update -  KCOCOTUP     
     
    Layer 4 : Transaction Block -  KCOCOTBK     [ktbcts.h]
         Opcode 1 : Block Cleanout 
         Opcode 2 : Physical Cleanout 
         Opcode 3 : Single Array Change
         Opcode 4 : Multiple Changes to an Array
         Opcode 5 : Format Block
     
    Layer 5 : Transaction Undo -  KCOCOTUN     [ktucts.h]
         Opcode 1 : Undo block or undo segment header - KTURDB
         Opcode 2 : Update rollback segment header - KTURDH
         Opcode 3 : Rollout a transaction begin 
         Opcode 4 : Commit transaction (transaction table update) 
- no undo record 
         Opcode 5 : Create rollback segment (format) - no undo record 
         Opcode 6 : Rollback record index in an undo block - KTUIRB
         Opcode 7 : Begin transaction (transaction table update) 
         Opcode 8 : Mark transaction as dead 
         Opcode 9 : Undo routine to rollback the extend of a rollback segment 
         Opcode 10 :Redo to perform the rollback of extend of rollback segment 
                    to the segment header. 
         Opcode 11 :Rollback DBA in transaction table entry - KTUBRB 
         Opcode 12 :Change transaction state (in transaction table entry) 
         Opcode 13 :Convert rollback segment format (V6 -> V7) 
         Opcode 14 :Change extent allocation parameters in a rollback segment 
         Opcode 15 :
         Opcode 16 :
         Opcode 17 :
         Opcode 18 :
         Opcode 19 : Transaction start audit log record
         Opcode 20 : Transaction continue audit log record     
         Opcode 24 : Kernel Transaction Undo Relog CHanGe - KTURLGU
    Layer 6 : Control File -  KCOCODCF     [tbs.h]
     
    Layer 10 : INDEX -  KCOCODIX     [kdi.h]
         Opcode 1 : load index block (Loader with direct mode) 
         Opcode 2 : Insert leaf row 
         Opcode 3 : Purge leaf row 
         Opcode 4 : Mark leaf row deleted 
         Opcode 5 : Restore leaf row (clear leaf delete flags) 
         Opcode 6 : Lock index block 
         Opcode 7 : Unlock index block 
         Opcode 8 : Initialize new leaf block 
         Opcode 9 : Apply Itl Redo 
         Opcode 10 :Set leaf block next link 
         Opcode 11 :Set leaf block previous link 
         Opcode 12 :Init root block after split 
         Opcode 13 :Make leaf block empty 
         Opcode 14 :Restore block before image 
         Opcode 15 :Branch block row insert 
         Opcode 16 :Branch block row purge 
         Opcode 17 :Initialize new branch block 
         Opcode 18 :Update keydata in row 
         Opcode 19 :Clear row's split flag 
         Opcode 20 :Set row's split flag 
         Opcode 21 :General undo above the cache (undo) 
         Opcode 22 :Undo operation on leaf key above the cache (undo) 
         Opcode 23 :Restore block to b-tree 
         Opcode 24 :Shrink ITL (transaction entries) 
         Opcode 25 :Format root block redo 
         Opcode 26 :Undo of format root block (undo) 
         Opcode 27 :Redo for undo of format root block 
         Opcode 28 :Undo for migrating block
         Opcode 29 :Redo for migrating block
         Opcode 30 :IOT leaf block nonkey update
         Opcode 31 :Cirect load root redo
         Opcode 32 :Combine operation for insert and restore rows 
     
    Layer 11 : Row Access -  KCOCODRW     [kdocts.h]
         Opcode 1 : Interpret Undo Record (Undo) 
         Opcode 2 : Insert Row Piece 
         Opcode 3 : Drop Row Piece 
         Opcode 4 : Lock Row Piece 
         Opcode 5 : Update Row Piece 
         Opcode 6 : Overwrite Row Piece 
         Opcode 7 : Manipulate First Column (add or delete the 1rst column) 
         Opcode 8 : Change Forwarding address 
         Opcode 9 : Change the Cluster Key Index 
         Opcode 10 :Set Key Links (change the forward & backward key links 
                    on a cluster key) 
         Opcode 11 :Quick Multi-Insert (ex: insert as select ...) 
         Opcode 12 :Quick Multi-Delete 
         Opcode 13 :Toggle Block Header flags 
     
    Layer 12 : Cluster -  KCOCODCL     [?]
    
    Layer 13 : Transaction Segment -  KCOCOTSG     [ktscts.h]
         Opcode 1 : Data segment format 
         Opcode 2 : Merge 
         Opcode 3 : Set link in block 
         Opcode 4 : Not used 
         Opcode 5 : New block (affects segment header) 
         Opcode 6 : Format block (affects data block) 
         Opcode 7 : Record link 
         Opcode 8 : Undo free list (undo) 
         Opcode 9 : Redo free list head (called as part of undo) 
         Opcode 9 : Format free list block (freelist group) 
         Opcode 11 :Format new blocks in free list 
         Opcode 12 :free list clear 
         Opcode 13 :free list restore (back) (undo of opcode 12) 
     
    Layer 14 : Transaction Extent -  KCOCOTEX     [kte.h]
         Opcode 1 : Add extent to segment 
         Opcode 2 : Unlock Segment Header 
         Opcode 3 : Extent DEaLlocation (DEL) 
         Opcode 4 : Undo to Add extent operation (see opcode 1) 
         Opcode 5 : Extent Incarnation number increment 
         Opcode 6 : Lock segment Header 
         Opcode 7 : Undo to rollback extent deallocation (see opcode 3) 
         Opcode 8 : Apply Position Update (truncate) 
         Opcode 9 : Link blocks to Freelist 
         Opcode 10 :Unlink blocks from Freelist 
         Opcode 11 :Undo to Apply Position Update (see opcode 8) 
         Opcode 12 :Convert segment header to 6.2.x type 
     
    Layer 15 : Table Space -  KCOCOTTS     [ktt.h]
        Opcode 1 : Format deferred rollback segment header 
        Opcode 2 : Add deferred rollback record 
        Opcode 3 : Move to next block 
        Opcode 4 : Point to next deferred rollback record 
     
    Layer 16 : Row Cache -  KCOCOQRC     
     
    Layer 17 : Recovery (REDO) -  KCOCORCV     [kcv.h]
         Opcode 1 : End Hot Backup : This operation clears the hot backup 
                    in-progress flags in the indicated list of files 
         Opcode 2 : Enable Thread : This operation creates a redo record 
                    signalling that a thread has been enabled 
         Opcode 3 : Crash Recovery Marker 
         Opcode 4 : Resizeable datafiles
         Opcode 5 : Tablespace ONline
         Opcode 6 : Tablespace OFFline
         Opcode 7 : Tablespace ReaD Write
         Opcode 8 : Tablespace ReaD Only
         Opcode 9 : ADDing datafiles to database
         Opcode 10 : Tablespace DRoP
         Opcode 11 : Tablespace PitR     
    Layer 18 : Hot Backup Log Blocks -  KCOCOHLB     [kcb.h]
         Opcode 1 : Log block image 
         Opcode 2 : Recovery testing 
     
    Layer 19 : Direct Loader Log Blocks - KCOCODLB     [kcbl.h]
         Opcode 1 : Direct block logging 
         Opcode 2 : Invalidate range 
         Opcode 3 : Direct block relogging
         Opcode 4 : Invalidate range relogging     
    Layer 20 : Compatibility Segment operations - KCOCOKCK  [kck.h]
         Opcode 1 : Format compatibility segment -  KCKFCS
         Opcode 2 : Update compatibility segment - KCKUCS
    Layer 21 : LOB segment operations - KCOCOLFS     [kdl2.h]
         Opcode 1 : Write data into ILOB data block - KDLOPWRI
    Layer 22 : Tablespace bitmapped file operations -  KCOCOTBF [ktfb.h]
 Opcode 1 : format space header - KTFBHFO
 Opcode 2 : space header generic redo - KTFBHREDO
 Opcode 3 : space header undo - KTFBHUNDO
 Opcode 4 : space bitmap block format - KTFBBFO
 Opcode 5 : bitmap block generic redo - KTFBBREDO 
    Layer 23 : write behind logging of blocks - KCOCOLWR [kcbb.h]
 Opcode 1 : Dummy block written callback - KCBBLWR
    Layer 24 : Logminer related (DDL or OBJV# redo) - KCOCOKRV [krv.h]
 Opcode : common portion of the ddl - KRVDDL
 Opcode : direct load redo - KRVDLR 
 Opcode : lob related info - KRVLOB
 Opcode : misc info - KRVMISC 
 Opcode : user info - KRVUSER
CV是组成REDO RECORD的基础,REDO RECORD是由一组CV组成的,这组CV完成对数据库的一个原子修改操作。举个例子,一个REDO RECORD里可能包含3CV,第一个是对UNDO SEGMENT HEADER的修改,第二个是对UNDO SEGMENT的修改,第三个是对DATA BLOCK的修改。而一个事务可能包含NREDO RECORD
当前台进程要对某个数据块进行修改的时候,首先要形成相关的CHANGE VECTOR,然后把多个CV组成REDO RECORD,把REDO RECORD写入LOG BUFFER后,前台进程可以将CV提交到相关的数据块上。
下面我们通过一个实验来学习一下REDO RECORDCHANGE VECTOR。我们设计的场景是,首先在SCOTT下创建一张表:SCOTT.T4
create table t4 (a integer);
然后我们查看一下当前的SCN是什么:
SQL> select max(ktuxescnw * power(2, 32) + ktuxescnb) from x$ktuxe;
MAX(KTUXESCNW*POWER(2,32)+KTUXESCNB)
------------------------------------
                            16230857
执行一条INSERT语句,然后以这条语句进行分析:
insert into t4 values (1);
commit;
 SQL> select max(ktuxescnw * power(2, 32) + ktuxescnb) from x$ktuxe;
MAX(KTUXESCNW*POWER(2,32)+KTUXESCNB)
------------------------------------
                            16230863
通过这两个SCN值,我们对当前的REDO LOG进行DUMP
SQL> alter system dump logfile 'd:\oracle\oradata\ora92\redo01.log' scn min 16230857 scn max 16230863;
系统已更改。
下面的内容就是我们刚才的INSERT语句产生的,下面是第一个REDO RECORD
REDO RECORD - Thread:1 RBA: 0x0000a1.000040ce.0010 LEN: 0x0054 VLD: 0x01  
SCN: 0x0000.00f7a9c9 SUBSCN:  1 03/12/2008 09:37:49
CHANGE #1 TYP:0 CLS:33 AFN:2 DBA:0x00800111 SCN:0x0000.00f7a9c7 SEQ:  1 OP:5.4              
可以看到这个REDO RECORDRBA0x0000a1.000040ce.0010,翻译成10进制是161.64.16LOG SEQUENCE号是161,在REDO LOG中的块号是64,起始字节是块内的16字节。这个REDO RECORD的长度是84字节(0X54)。
VLD:0X01表示这个REDO RECORD的类型,0X01表示CHANGE VECTOR COPIED IN。再往下看CHANGE #1,这就是这个REDO RECORD的第一个CV,我们看到OP:5.4,根据上面的OPCODE清单我们可以看到是Commit transaction (transaction table update) ,修改事务表,RDBA2/273通过DBA_EXTENTS查询为_SYSSMU3$
继续看下一个REDO RECORD:
REDO RECORD - Thread:1 RBA: 0x0000a1.000040cf.0010 LEN: 0x0058 VLD: 0x02  
SCN: 0x0000.00f7a9cb SUBSCN:  1 03/12/2008 09:37:57
CHANGE #1 MEDIA RECOVERY MARKER SCN:0x0000.00000000 SEQ:  0 OP:23.1   
 Block Written - afn: 2 rdba: 0x008095f1(2,38385)                     ----undo segment
                   scn: 0x0000.00f79cf1 seq: 0x02 flg:0x04
 Block Written - afn: 2 rdba: 0x0080726b(2,29291)                    ------undo segment 
                   scn: 0x0000.00f7a1e1 seq: 0x02 flg:0x04           ------undo segment
 Block Written - afn: 2 rdba: 0x00806e2e(2,28206)                    ------undo segment
                   scn: 0x0000.00f79d50 seq: 0x02 flg:0x04
这个REDO RECORD的VLD是2,含义是A commit SCN was allocated and is stored。说明这个REDO RECORD里分配并存储了一个SCN。我们来看第一个CHANGE:
  OP:23.1,OPCODE是23.1,Dummy block written callback - KCBBLWR,是产生一些WRITE BEHIND LOGGING信息。下一个REDO RECORD开始是针对T4表的操作。
REDO RECORD - Thread:1 RBA: 0x0000a1.000040d0.0010 LEN: 0x00b8 VLD: 0x01  
SCN: 0x0000.00f7a9cd SUBSCN:  1 03/12/2008 09:38:03
CHANGE #1 TYP:0 CLS: 4 AFN:5 DBA:0x01401a63 SCN:0x0000.00f7a965 SEQ:  3 OP:13.28  
Redo on Level1 Bitmap Block  ---针对1ST BMB的操作
Redo to add range
bdba: Length: 16
CHANGE #2 TYP:0 CLS: 8 AFN:5 DBA:0x01401a61 SCN:0x0000.00f7a965 SEQ:  2 OP:13.22  ----dba (5/6753) -- scott.t4的segment header,设置高水位
Redo on Level1 Bitmap Block
Redo to set hwm
Opcode: 32      Highwater::  0x01401a71  ext#: 0      blk#: 16     ext size: 16    
  #blocks in seg. hdr's freelists: 0     
  #blocks below: 13    
  mapblk  0x00000000  offset: 0     
  
这个REDO RECORD是对SCOTT.T4的表头的操作。下面连续几个REDO RECORD是格式化数据块,为了简化起见,我只列出其中的一个:
REDO RECORD - Thread:1 RBA: 0x0000a1.000040d0.00c8 LEN: 0x003c VLD: 0x01
SCN: 0x0000.00f7a9cd SUBSCN:  1 03/12/2008 09:38:03
CHANGE #1 TYP:1 CLS: 1 AFN:5 DBA:0x01401a64 SCN:0x0000.00f7a9cd SEQ:  1 OP:13.21  --dba(5/6756) --scott.t4,格式化BLOCK
ktspbfredo - Format Pagetable Datablock 
Parent(l1) DBA: typ: 1 objd: 32027 itls: 2 fmt_flag: 0 poff: 0
中间我省略了几个REDO RECORD,直接看看包含INSERT语句的REDO RECORD:
REDO RECORD - Thread:1 RBA: 0x0000a1.000040d2.00b0 LEN: 0x015c VLD: 0x01
SCN: 0x0000.00f7a9cd SUBSCN:  1 03/12/2008 09:38:03
CHANGE #1 TYP:0 CLS:23 AFN:2 DBA:0x00800071 SCN:0x0000.00f7a38b SEQ:  1 OP:5.2    ----Update rollback segment header - KTURDH SYS_SYSSMU2$
ktudh redo: slt: 0x000f sqn: 0x00004947 flg: 0x0012 siz: 80 fbi: 0
            uba: 0x008090cb.0550.13    pxid:  0x0000.000.00000000
CHANGE #2 TYP:0 CLS:24 AFN:2 DBA:0x008090cb SCN:0x0000.00f7a38a SEQ:  3 OP:5.1   ---Undo block or undo segment header - KTURDB  SYS_SYSSMU4$
ktudb redo: siz: 80 spc: 2746 flg: 0x0012 seq: 0x0550 rec: 0x13
            xid:  0x0004.00f.00004947  
ktubl redo: slt: 15 rci: 0 opc: 11.1 objn: 32027 objd: 32027 tsn: 5              ----Interpret Undo Record (Undo) ,针对scott.t4表生成UNDO数据
Undo type:  Regular undo        Begin trans    Last buffer split:  No 
Temp Object:  No 
Tablespace Undo:  No 
             0x00000000  prev ctl uba: 0x008090cb.0550.10 
prev ctl max cmt scn:  0x0000.00f78d7e  prev tx cmt scn:  0x0000.00f78f06 
KDO undo record:
KTB Redo 
op: 0x03  ver: 0x01  
op: Z                                                                          ------Undo of first (ever) change to the ITL,首先是对ITL的修改
KDO Op code: DRP row dependencies Disabled                                     ------Delete Row Piece
  xtype: XA  bdba: 0x01401a65  hdba: 0x01401a63                                  -----ROWID
itli: 1  ispac: 0  maxfr: 2401
tabn: 0 slot: 0(0x0)
CHANGE #3 TYP:0 CLS: 1 AFN:5 DBA:0x01401a65 SCN:0x0000.00f7a9cd SEQ:  2 OP:11.2   ---Insert Row Piece ,插入一条记录
KTB Redo 
op: 0x01  ver: 0x01  
op: F  xid:  0x0004.00f.00004947    uba: 0x008090cb.0550.13                       ---First change to ITL by this TX. Copy redo to ITL
KDO Op code: IRP row dependencies Disabled                                        ---Single Insert Row Piece,行插入操作
  xtype: XA  bdba: 0x01401a65  hdba: 0x01401a63                                   ---对应的表是scott.t4
itli: 1  ispac: 0  maxfr: 2401
tabn: 0 slot: 0(0x0) size/delt: 6
fb: --H-FL-- lb: 0x1  cc: 1
null: -
col  0: [ 2]  c1 02                                                                                         ----十进制1,就是我们插入的数据
CHANGE #4 MEDIA RECOVERY MARKER SCN:0x0000.00000000 SEQ:  0 OP:5.20              ----Transaction continue audit log record,记录SESSION信息
session number   = 11
serial  number   = 115
transaction name = 
这个事务的最后一个REDO RECORD就是COMMIT产生的记录:
REDO RECORD - Thread:1 RBA: 0x0000a1.000040d4.0010 LEN: 0x0054 VLD: 0x01        
SCN: 0x0000.00f7a9cf SUBSCN:  1 03/12/2008 09:38:03
CHANGE #1 TYP:0 CLS:23 AFN:2 DBA:0x00800071 SCN:0x0000.00f7a9cd SEQ:  1 OP:5.4  ----Commit transaction (transaction table update) 
ktucm redo: slt: 0x000f sqn: 0x00004947 srt: 0 sta: 9 flg: 0x2 
ktucf redo: uba: 0x008090cb.0550.13 ext: 2 spc: 2664 fbi: 0