天天看點

Oracle block scn什麼時候發生變化

我們知道block scn存在 block頭中,其具體位置在block offset 8-13中,即占用6個位元組。

用bbed檢視,可以看到scn處于kcbh結構體中,其中offset 8-11屬于scn的低8位,offfset 12-13屬于scn的高4位。

struct kcbh, 20 bytes @0

ub1 type_kcbh @0

ub1 frmt_kcbh @1

ub1 spare1_kcbh @2

ub1 spare2_kcbh @3

ub4 rdba_kcbh @4

[color=red] ub4 bas_kcbh @8

ub2 wrp_kcbh @12 [/color]

ub1 seq_kcbh @14

ub1 flg_kcbh @15

ub2 chkval_kcbh @16

ub2 spare3_kcbh @18

那我們不禁有個疑問?此scn是什麼時候産生的呢?是該block發生變化時,還是該block從buffer_cache刷到資料檔案時産生的呢?

我們先做簡要分析

假設會話開始于scn 1000,block scn記錄的是block變化時scn

(1)不論引起該block變化的事務送出與否,當block scn大于1000時,那麼會話将讀取該block後,為保證事務一緻性會話将讀取undo block(即consistent read)。

(2)不論引起該block變化的事務送出與否,當block scn小于1000時,那麼會話将讀取該block後,如果事務已送出,将直接讀取結果。如果事務未送出,那再次讀取undo block,構造一緻性block。

采用此方法,當有多并發事務時,原理相似,并不會導緻事務不一緻。

如果block scn記錄的是記憶體刷到資料檔案時的current scn,那會有什麼樣的後果?

假設block變化scn為1500。block scn記錄的是從block從buffer_cache刷到資料檔案時産生,其scn為2000并已記錄在block head中,且此block已不在記憶體中。

假設會話開始于scn 1600,當已引起該block變化的事務已送出,從事務一緻性角度來講,将直接讀取block(即current read),但由于block head記錄為scn 2000,

1600<2000,又滿足consistent read條件(一直讀到scn<1600,且事務已送出的scn為止),這樣又會引起事務不一緻。

經過以上分析,我們得出以下結論,block head記錄的是該引起該block變化時的scn。

下面通過實驗來解答上述結論。以下測試來自測試環境,資料庫極少事務變化量。

首先檢視表格zhoul在資料庫的存放位置,由以下查詢可知zhoul表格資料存放在7号檔案block号為15511的資料塊中。

SQL> col file# for 999

SQL> col block# for 99999

SQL> set linesize 300

SQL> select dbms_rowid.ROWID_RELATIVE_FNO(rowid) file#,dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block#,i,name from zhoul;

FILE# BLOCK# I NAME

----- ------ ---------- --------------------

7 15511 1 aaa

7 15511 2 bbb

7 15511 3 ccc

為了獲得比較幹淨的測試環境,首先切換一個歸檔日志,這樣可以将其他事務的變化條目排除在這個online redolog之外。

SQL> alter system switch logfile;

System altered.

SQL> select * from zhoul;

I NAME

---------- --------------------

1 uuu

2 bbb

3 ccc

在記憶體中修改表格zhoul資料,注意将字段i=1修改成系統最新的scn值,并進行送出。這樣該資料檔案頭在buffer_cache存儲的scn将會比10995251185389略大

但應該會比10995251185563小。

SQL> update zhoul set i=(select current_scn scn from v$database) where i=1;

1 row updated.

SQL> commit;

Commit complete.

SQL> col i for 999999999999999999

SQL> select * from zhoul;

I NAME

------------------- --------------------

10995251185389 uuu

2 bbb

3 ccc

SQL> select current_scn i from v$database;

I

-------------------

10995251185563

打開statistic跟蹤,可以看到全部為8個consistent gets,也就意味着15511号還在buffer_cache中。

SQL> set autot traceonly stat

SQL> select * from zhoul;

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

8 consistent gets

0 physical reads

0 redo size

523 bytes sent via SQL*Net to client

400 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

3 rows processed

現在将buffer_cache中資料塊刷出至資料檔案中。

SQL> alter system flush buffer_cache;

System altered.

獲得包含此事務的online redolog

SQL> set autot off

SQL> select member from v$log a,v$logfile b where a.group#=b.group# and a.status='CURRENT';

MEMBER

--------------------------------------------------------------------------------

/oradata/mcstar/redo01.log

将redo01.log dump出來,由于本文隻研究資料塊寫出操作,固隻需dump layer為23,opcode為1的change。

SQL> alter system dump logfile '/oradata/mcstar/redo01.log' layer 23 opcode 1;

System altered.

打開跟蹤檔案可以看到,其scn為 0x0a00.080a86ef,此值和bbed結果一緻。

CHANGE #1 MEDIA RECOVERY MARKER SCN:0x0000.00000000 SEQ: 0 OP:23.1

Block Written - afn: 7 rdba: 0x01c03c97 BFT:(1024,29375639) non-BFT:(7,15511)

scn: 0x0a00.080a86ef seq: 0x03 flg:0x06

BBED> dump block 15511 offset 0 count 20

File: /oradata/mcstar/zhoul01.dbf (0)

Block: 15511 Offsets: 0 to 19 Dba:0x00000000

------------------------------------------------------------------------

06a20000 973cc001 ef860a08 000a0306 3f130000

進一步将0x0a00.080a86ef轉換成10進制之後為10995251185391,此值比10995251185389略大,但小于10995251185563,也就證明了我們的猜想:

block head的scn記錄的是該block改變時的scn,并非從buffer_cache時刷出的scn。

SQL> col scn for 999999999999999

SQL> select to_number('0a00080a86ef','xxxxxxxxxxxx') scn from dual;

SCN

----------------

10995251185391