理论解析:
1)server process 搜索buffer cache中是否有update要更新的block(内存中又名buffer),没有则从disk读入buffer cache
2)server process 构造一组change vector来记录对数据块的改动(此时放于PGA中),这组change vector组成 redo record
这里可能有4个数据块会被改动:
回滚段的段头块:相应的事务表上标注事务信息,其中包括了xid,uba等;并且分配回滚块
数据块的块头块:分配事务槽,标注事务信息,其中包含了xid,uba等
回滚块:记录前镜像
数据块:修改
3)用redo record 的大小判断需要多少redo log buffer空间
4)判断current scn,将其存到redo record(不同的redo record可能会共享同样的SCN值,用change vector header里面的值SEQ加以区别)
5)获得redo copy latch,接着获得redo allocation latch
6)查看当前是否有其它process比当前持有的SCN 更高的SCN值,有的话生成一个新SCN,替换第4步中的SCN。总之,保存最新的scn。
7)判断是否有足够的redo log buffer空间,具体如下:
如果redo log buffer没有足够空间,释放redo copy latch,redo allocation latch,释放后如果此时有其它进程或条件出发了LGWR则要等待lgwr完成,如果此时LGWR没启动,则触发lgwr 启动(需要redo writing latch,从LOG BUFFER 把redo record写入online current redo log file中时 LGWR进程会一直占着这个LATCH,防止其他process同时触发LGWR,其它process要尝试获得该latch必须等待,还会获得redo allocation latch 防止有新的change vector继续写入log buffer,造成LGWR无法确定应该写多少redo),当没有足够空间时,释放完redo copy/allocation latch后,会立刻尝试获取redo writing latch 获取不到 就表明lgwr被其他process启动了,等待,继续尝试,获取到后会先检查log buffer是否有空间了(因为前面LGWR 可能释放了大量redo log buffer空间)此时要是有空间了 释放redo writing latch,然后获得redo copy/allocation latch,没空间的话触发lgwr,并且释放redo writing latch(把这个latch给了lgwr).如果没有空间,且lgwr 将redo log buffer 里面的redo record 写入online current redo logfile时 ,redo logfile空间不足不够写入时,则process会检查是否有其它process启动了switch logfile,如果没有则触发switch logfile,有其他进程启动了,则当前进程等待,switch logfile 后,继续让lgwr写入redo log buffer .(注意switch log时候 禁止生成redo)
如果redo log buffer有足够的空间,则分配空间, 释放redo allocation latch,然后把change vector从PGA COPY到 redo log buffer,把redo record 对应在buffer cache中修改的block 挂到CKPTQ(checkpoint queue)上去(等dbwr写入)然后释放redo copy latch.
8)检查写完后的redo log buffer ,redo record是否达到threadshold,到了则触发lgwr
9)更新buffer cache 中block
具体实验(dump undo信息来理解):
1)
SQL> conn hr/hr 已连接。 SQL> drop table t1; drop table t1 * 第 1 行出现错误: ORA-00942: 表或视图不存在 SQL> create table t1(id number,name varchar2(20)); 表已创建。 SQL> insert into t1(1,'a'); insert into t1(1,'a') * 第 1 行出现错误: ORA-00928: 缺失 SELECT 关键字 SQL> insert into t1 values(1,'a'); 已创建 1 行。 SQL> insert into t1 values(2,'b'); 已创建 1 行。 SQL> commit; 提交完成。
2)
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno, 2 dbms_rowid.rowid_block_number(rowid) bno 3 from hr.t1; FNO BNO ---------- ---------- 4 437 4 437 SQL> select data_object_id,owner from dba_objects where object_name='T1'; DATA_OBJECT_ID OWNER -------------- ------------------------------------------------------------ 53467 HR SQL> alter system flush buffer_cache; 系统已更改。 SQL> select file#,block#,dirty from v$bh where objd=53467; FILE# BLOCK# DI ---------- ---------- -- 4 438 N 4 433 N 4 436 N 4 439 N 4 434 N 4 437 N 已选择6行。
3)
SQL> select * from t1; ID NAME ---------- ---------------------------------------- 1 a 2 b SQL> update t1 set name='c' where id=1; 已更新 1 行。
4)
SQL> select file#,block#,dirty from v$bh where objd=53467; FILE# BLOCK# DI ---------- ---------- -- 4 440 N 4 435 N 4 438 N 4 438 N 4 433 N 4 436 N 4 436 N 4 439 N 4 439 N 4 434 N 4 437 N 4 437 N 4 437 Y 已选择13行。 SQL> select xidusn,xidslot,xidsqn,ubafil,ubablk from v$transaction; XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK ---------- ---------- ---------- ---------- ---------- 8 6 579 2 2636
server process 找出4号文件、第437个块放入buffer cache里,并且找到一个可用的undo块:第2636个块,将旧值存进去,产生change vector,然后将新值存进第437个块,产生change vector。
5)
SQL> conn / as sysdba 已连接。 SQL> select * from hr.t1; ID NAME ---------- ---------------------------------------- 1 a 2 b SQL> update hr.t1 set name='d' where id=2; 已更新 1 行。
6)
SQL> select file#,block#,dirty from v$bh where objd=53467; FILE# BLOCK# DI ---------- ---------- -- 4 440 N 4 435 N 4 438 N 4 438 N 4 433 N 4 436 N 4 436 N 4 439 N 4 439 N 4 434 N 4 437 N 4 437 N 4 437 N 4 437 N 4 437 Y 4 437 N 4 437 N 已选择17行。 SQL> select xidusn,xidslot,xidsqn,ubafil,ubablk from v$transaction; XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK ---------- ---------- ---------- ---------- ---------- 9 3 607 2 2184 --T2 8 6 579 2 2636 --T1
此时事务T1提交,LGWR会将事务T2的redo entries一起写入log file
7)
SQL> select name,usn from v$rollname where usn in (8,9); NAME USN ------------------------------------------------------------ ---------- _SYSSMU8$ 8 _SYSSMU9$ 9
8)
dump udno段8 SQL> select spid from v$process where addr in (select paddr from v$session where sid in (select sid from v$mystat where 2 rownum=1)); SPID ------------------------ 5316 SQL> alter system dump undo header '_SYSSMU8$'; 系统已更改。 dump undo段9 SQL> select spid from v$process where addr in (select paddr from v$session where sid in (select sid from v$mystat where rownum=1)); SPID ------------------------ 5544 SQL> alter system dump undo header '_SYSSMU9$'; 系统已更改。
9)
_SYSSMU8$的dump TRN TBL:: index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt 0x06 10 0x80 0x0243 0x0002 0x0000.00215819 0x00800a4c 0x0000.000.00000000 0x00000001 0x00000000 0 xidslot为0x06,即事务表第6行。state=10代表有活动事务(9则表示已经提交) _SYSSMU9$的dump和dump8一样,在此不表。
根据日志文件的信息来理解,待续...........