一. Block Cleanout 說明
文章的整理參考:http://www.orawh.com/60.html
block clean out 是指把一個塊中的資料從 dirty 變為 clean,等於告訴後面的人,這個塊裡面的資料是乾淨的,可以放心的使用,本質上是更改 block header 中的一個標誌位。
當commit 的時候,如果被commit 的資料區塊還在 data buffer 中也要被cleanout,因為 commit 的時候並不一定修改block header (delay block cleanout) 。
Clean out有2種: fast commitcleanout和delayed blockcleanout:
oracle有一個modified block list結構,用來記錄每個transaction更改過的block,每個transaction大約可以記錄10%buffer cache這多的modified block。這部分block就是當發生commit的時候,oracle可以根據modified block list定位到那些塊並做fast commit cleanout。
如果一個transaction修改的塊超過10%buffer cache,那麼超過的塊就執行delayed block cleanout。當做fast commit cleanout時,oracle不會清理 Row lockslb標誌位,ITL lck標誌位。
另一種情況是delayed block cleanout,當transaction還未commit或rollback時modified block已經被寫回磁碟,當發生commit時oracle並不會把block重新讀入做cleanout,這樣成本太高.而是把cleanout留到下一次對此塊的訪問(select,update)時完成。
當delayed cleanout時候如果undo segment header的transaction table slot還沒有被覆蓋,那麼可以找回該事務遞交的exact scn,如果slot已經被覆蓋(ITL被覆蓋),那麼將會使用undo segment header中的control scn來做為upper bound scn。
當發生fast commit cleanout,系統將transaction提交時刻的scn作為commit scn,更新block上 itl和undo segment header的Transaction table的slot上的 scn,並修改block scn,三者是一致的。
發生delayed block cleanout的時候,之前的transaction commit更新的只是undo segment header Transactiontable 上的slot scn,而並未做block上的更新,等待下次使用此block的時候,更新block scn和itl狀態。block scn和itl的更新又分2種情況:
(1)當不產生slot重用的時候, delayedblock cleanout時,根據Transactiontable裡面的資訊,更新blockscn和itl上的Scn/Fsc為transaction曾經提交時候的scn。
(2)當產生slot重用的時候,更新對應itl上scn為undo segment 上的control scn,而block scn 為delayed block cleanout發生時刻的scn。
二. Cleanout 測試2.1 Fast commit cleanout
--建立table並insertinto data
SYS@anqing2(rac2)> create table dvd(idnumber);
Table created.
SYS@anqing2(rac2)> insert into dvdvalues(1);
1 row created.
SYS@anqing2(rac2)> insert into dvdvalues(2);
1 row created.
SYS@anqing2(rac2)> commit;
Commit complete.
SYS@anqing2(rac2)>
--查看table 的block資訊
SYS@anqing2(rac2)> Selectdbms_rowid.rowid_block_number(rowid) block,dbms_rowid.rowid_relative_fno(rowid) fileno, ora_rowscn from dvd;
BLOCK FILENO ORA_ROWSCN
---------- ---------- ----------
305914 1 7316063
305914 1 7316063
--更新並提交資料
SYS@anqing2(rac2)> update dvd set id=77where id=1;
1 row updated.
SYS@anqing2(rac2)> commit;
Commit complete.
--Dump block
SYS@anqing2(rac2)> oradebug setmypid
Statement processed.
SYS@anqing2(rac2)> alter system dumpdatafile 1 block 305914;
System altered.
SYS@anqing2(rac2)> oradebugtracefile_name
/u01/app/oracle/admin/anqing/udump/anqing2_ora_31100.trc
Dump 檔案的ITL 資訊如下:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0010.011.000003c6 0x01400038.00ae.2d C--- 0 scn 0x0000.006fa25f
0x02 0x0011.01c.000004a9 0x01400070.0111.26 --U- 1 fsc 0x0000.006fa357
此時的操作就是fast commit cleanout,並且該操作不清除lck,lb標誌。
2.2 Delayed block cleanout
當我們update 資料之後,並且沒有commit,此時我們flush buffer cache,將修改的資料區塊,flush 到硬碟,那麼此時發生的就是delay block cleanout。
SYS@anqing2(rac2)> update dvd set id=168where id=2;
1 row updated.
SYS@anqing2(rac2)> Selectxidusn,xidslot,xidsqn from v$transaction;
XIDUSN XIDSLOT XIDSQN
---------- ---------- ----------
13 15 980
SYS@anqing2(rac2)> alter system flushbuffer_cache;
System altered.
--flush 會把我們之前修改的block 直接flush 到硬碟,雖然我們沒有commit。
SYS@anqing2(rac2)> commit;
Commit complete.
--此時我們commit了,正常情況下,會去修改block裡的相關SCN。 但是實際上此時Oracle 並沒有回去修改這些block,因為再次調用成本太大。 Oracle只更新了undosegment header slot。 當下次再次訪問這個block時,在根據undo segment 來更新block scn 和 itl 上的scn。 如果此時對應的undo segment 已經不存在,就會出發ORA-01555,快照過舊的錯誤。
--此時還沒有再次訪問之前的block,即沒有發生delayedblock clean,我們dump 一下資料區塊
SYS@anqing2(rac2)> alter system dumpdatafile 1 block 305914;
System altered.
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000d.00f.000003d4 0x0140002e.00aa.21 ----
1 fsc 0x0000.00000000
0x02 0x0011.01c.000004a9 0x01400070.0111.26 --U-
1 fsc 0x0000.006fa357
tab 0, row 0, @0x1f9a
tl: 6 fb: --H-FL-- lb: 0x2 cc: 1
col 0: [ 2] c1 4e
tab 0, row 1, @0x1f8d
tl: 7 fb: --H-FL-- lb: 0x1 cc: 1
col 0: [ 3] c2 02 45
--結果是fast commit cleanout
--訪問之前之前的block,產生delayedblock cleanout
SYS@anqing2(rac2)> select * from dvd;
ID
----------
77
168
--在次dump block
SYS@anqing2(rac2)> alter system dumpdatafile 1 block 305914;
System altered.
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000d.00f.000003d4 0x0140002e.00aa.21 C---
0 scn 0x0000.006fa4e6
0x02 0x0011.01c.000004a9 0x01400070.0111.26 C---
0 scn 0x0000.006fa357
tab 0, row 0, @0x1f9a
tl: 6 fb: --H-FL-- lb: 0x0 cc: 1
col 0: [ 2] c1 4e
tab 0, row 1, @0x1f8d
tl: 7 fb: --H-FL-- lb:0x0 cc: 1
col 0: [ 3] c2 02 45
--做了delayed block cleanout之後,itl 變成了SCN。 此時lck,lb標誌為都被清零,scn也是從undo segment header transactiontable slot裡面得到。如果undosegment header 上的slot被覆蓋了,那麼會把undo segment 上的control scn拿來當作upper bound scn。
(1) fb :
K = Cluster Key(Flags may change meaning ifthis is set to show HASH cluster)
C = Cluster table member
H = Head piece of row
D = Deleted row
F = First data piece
L = Last data piece
P = First column continues from previous piece
N = Last column continues in next piece
(2)lb : 和上面的 ITL 的lck相對應表示這行是否被 lock 了
在汪海的blog 上,他還測試了另一個結論:
當delayed block cleanout 發生時,依賴與undo segment來保證,如果undo segment 被刪除了,那麼會Oracle 會使用system 資料表空間下的undo$ 基表來保證delayed block cleanout。
關於這個測試過程,參考汪海的blog:http://www.orawh.com/60.html
三. Delayed block cleanout 與select redo 說明與測試
一般來說,select 是不會產生redo的。 但如果發生了delayed block cleanout,那麼就會產生redo。 當然這隻是一種情況,開啟審計等,也會造成select 產生redo。
在itpub上有2個相關的連結:
http://www.itpub.net/thread-1467473-1-2.html
http://www.itpub.net/thread-728163-1-1.html
下面我們測試一下。
--先對dvd insert 一些測試資料
SYS@anqing2(rac2)> Declare
2 I number;
3 Begin
4 For I in 1..100 loop
5 Insert into dvd values(i);
End loop;
Commit;
End;
/
6 7 8 9
PL/SQL procedure successfully completed.
SYS@anqing2(rac2)> select count(*) fromdvd;
COUNT(*)
----------
102
--直接select
SYS@anqing2(rac2)> set timing on
SYS@anqing2(rac2)> set autot on stat
SYS@anqing2(rac2)> select count(*) fromdvd;
COUNT(*)
----------
102
Elapsed: 00:00:00.00
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
412 bytes sent via SQL*Net toclient
400 bytes received via SQL*Netfrom client
2 SQL*Net roundtrips to/fromclient
0 sorts (memory)
0 sorts (disk)
1 rows processed
測試產生的redo 為0.
--製造delayedblock cleanout
SYS@anqing2(rac2)> update dvd set id=0where id>50;
52 rows updated.
Elapsed: 00:00:00.02
Statistics
----------------------------------------------------------
5 recursive calls
52 db block gets
8 consistent gets
0 physical reads
14424 redo size
667 bytes sent via SQL*Net toclient
564 bytes received via SQL*Netfrom client
3 SQL*Net roundtrips to/fromclient
1 sorts (memory)
0 sorts (disk)
52 rows processed
SYS@anqing2(rac2)> alter system flush buffer_cache;
System altered.
Elapsed: 00:00:00.10
SYS@anqing2(rac2)> commit;
Commit complete.
Elapsed: 00:00:00.02
--再此select
SYS@anqing2(rac2)> select count(*) fromdvd;
COUNT(*)
----------
102
Elapsed: 00:00:00.02
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4 consistent gets
2 physical reads
116 redo size
412 bytes sent via SQL*Net toclient
400 bytes received via SQL*Netfrom client
2 SQL*Net roundtrips to/fromclient
0 sorts (memory)
0 sorts (disk)
1 rows processed
--第三次select
SYS@anqing2(rac2)> select count(*) fromdvd;
COUNT(*)
----------
102
Elapsed: 00:00:00.00
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
412 bytes sent via SQL*Net toclient
400 bytes received via SQL*Netfrom client
2 SQL*Net roundtrips to/fromclient
0 sorts (memory)
0 sorts (disk)
1 rows processed
第二次select 因為delayed block cleanout的原因,需要做一些善後工作,所以產生了redo。 當第三次select 時,第二次select 已經把工作做完了,所以沒有產生redo。
簡單的就是說: select 在delayed block cleanout 時也會產生redo。