log buffer及日誌管理深入分析及效能調整(三)

聽海★藍心夢發表於2009-03-17
2.    <!--[endif]--&gtlog buffer的內部機制

日誌緩衝區的內部管理分為兩部分,一部分是重做記錄的生成,另一部分就是重做記錄寫入聯機日誌

檔案。這兩部分不是孤立的,沒有關聯的。在生成重做記錄的過程中,可能會觸發LGWR將重做記錄寫入聯機日誌檔案。

      我們先用一個例項來說明在日誌緩衝區中的操作過程,並使用[file# , blk#]來表示某個資料塊,file#表示檔案號,blk#表示資料塊號。

假設session 1發出更新語句:update redo_test set name='cdf' where id=1;

   首先找出id=1所在的資料塊(假設為[file#4,blk#120])放入buffer cache,然後找出一個可用的回滾段資料塊(假設為[file#2,blk#19]),將舊值'abc'放入該塊,同時生成重做記錄。然後將'cdf'放入表的資料塊,再生成重做記錄。這時的日誌緩衝區的結構類似如下。(我們從前面描述日誌緩衝區的記憶體結構時,知道重做記錄中最重要的就下面列的這幾列內容。同時,下面的一行就表示一個改動向量):

行號   事務id     file#  block# row    column     value

1      T1         2      19     -      -          abc

2      T1         4      120    1      2          cdf

   這時假設session 2發出其他更新語句:update t set c1=10 where c1=9;

   同樣的道理,oracle找到該資料塊(假設為[file#5,blk#200])放入buffer cache,並找到回滾段資料塊(假設為[file#2,blk#30])存放舊值,生成重做記錄,更新表的資料塊,再次生成重做記錄。這時的日誌緩衝區的結構類似如下:

行號   事務id     file#  block# row    column     value

1      T1         2      19     -      -          abc

2      T1         4      120    1      2          cdf

3      T20        2      30     -      -          9

4      T20        5      200    20     1          10

             這時,session 1又發出更新語句:update redo_test set name='xyz1' where id=2,並提交(commit)。

       同樣的方式處理回滾段和資料塊,並生成重做記錄。假設這時生成日誌緩衝區假設為:

行號   事務id     file#  block# row    column     value

1      T1         2      19     -      -          abc

2      T1         4      120    1      2          cdf

3      T20        2      30     -      -          9

4      T20        5      200    20     1          10

5      T1         2      19     -      -          abc

6      T1         4      120    2      2          xyz1

7      T1         commit SCN    timestamp

   這時,我們可以注意到,提交標記也被記錄到了重做記錄中。從我們前面轉儲出的結果中也可以看到“sta: 9”的內容,這就是提交標記。每次提交時,都會生成一個SCN號,SCN號越小,說明發生的越早,其所屬的重做記錄就越排在前面。一旦使用者發出commit語句,系統就會觸發LGWR程式。這時,LGWR程式會將上面所顯示的所有重做記錄都寫入聯機日誌檔案中。注意,其中也包括尚未提交的事務T20

   LGWR寫這些重做記錄的過程中,又有其他session發出更新語句,並提交。這時的日誌緩衝區假設如下所示:

行號   事務id     file#  block# row    column     value

1      T1         2      19     -      -          abc

2      T1         4      120    1      2          cdf

3      T20        2      30     -      -          9

4      T20        5      200    20     1          10

5      T1         2      19     -      -          abc

6      T1         4      120    2      2          xyz1

7      T1         commit SCN    timestamp                以上的重做日誌正在由LGWR寫入

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

8      T20        2      39     -      -          289  LGWR寫時生成以下的重做日誌

9      T20        5      498    220    3          190

10     T9         2      90     -      -          hhh

11     T9         9      100    20     9          xxx

12     T9         commit SCN     timestamp

13     T18        2      189    -      -          18

14     T18        10     29     300    10         20

15     T18        commit SCN     timestamp

LGWR寫完第一批重做記錄1到第7以後,就會立即開始寫第二批重做記錄(第8行到第15行)。注意,第二批重做記錄中,存在兩個commit,但LGWR不會分成兩次來寫,而是一次就將它們全部寫入。當LGWR在寫完第1到第7行的改動向量以後,這部分的日誌緩衝區記憶體就被釋放了,可以被新生成的重做記錄所覆蓋。

如果當前聯機日誌檔案寫完時,這時就需要轉換到另外一個可寫的聯機日誌檔案上去,這個過程叫做日誌切換。日誌切換的大致過程包括:

<!--[if !supportLists]--&gt1)     <!--[endif]--&gt從控制檔案中得到下一個可用的聯機日誌檔案。

<!--[if !supportLists]--&gt2)     <!--[endif]--&gt記錄寫入當前聯機日誌檔案的最後一個日誌塊的SCN(叫做high SCN)。關閉當前聯機日誌檔案。

<!--[if !supportLists]--&gt3)     <!--[endif]--&gt增加SCN再次操作控制檔案將下一個聯機日誌檔案標誌為CURRENT判斷前一個聯機日誌檔案裡包含的重做記錄所對應的髒資料塊是否都已經寫入資料檔案,如果沒有則標記ACTIVE;如果是則標記為INACTIVE如果是歸檔模式那麼LGWR將前一個聯機日誌檔案加入歸檔列表中等待歸檔。

<!--[if !supportLists]--&gt4)     <!--[endif]--&gt開啟新的聯機日誌檔案組中的所有成員,記錄當前日誌序列號(log sequence)和第一個日誌塊的SCN(叫做low SCN),新一輪的重做記錄開始。

我們經常看到當前日誌檔案的狀態為CURRENT,而前一個日誌檔案的狀態為ACTIVE的情況。實際上,這是由於記憶體中存在很多髒資料塊,而髒資料塊的寫入是透過DBWR程式完成的。如果髒資料塊沒有積累到一定的量,DBWR是不會將它們寫入資料檔案的。所以,ACTIVE狀態的日誌檔案表示該日誌檔案裡包含的重做記錄所對應的髒資料塊還沒有被DBWR程式寫入資料檔案。事實上,日誌切換時觸發增量檢查點(incremental checkpoint),CKPT將會觸發DBWR寫髒資料塊。增量檢查點只是在控制檔案中記錄這時在檢查點佇列上的髒資料塊在第一次修改時所對應的日誌塊在日誌檔案中的地址,這個地址叫做檢查點位置(checkpoint position)。但是DBWR啟動並不表示立即寫髒資料塊,除非髒資料塊的數量達到一定程度,或超過一定時間等。我們來模擬一下這種情況。

SQL> select group#,status from v$log;

   GROUP# STATUS

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

        1 CURRENT

        2 INACTIVE

        3 INACTIVE

SQL> create table t2 as select * from dba_objects;

SQL> select group#,status from v$log;

   GROUP# STATUS

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

        1 ACTIVE

        2 CURRENT

        3 INACTIVE

我們可以看到,當建立了表t2的時候,發生了日誌切換。如果沒有看到日誌切換,可以再刪除表t2然後再建立表t2,如此反覆幾次,就能夠引起日誌切換。這時我們看到2號日誌檔案為當前開啟的日誌檔案,其狀態為CURRENT。而1號日誌檔案為上次開啟的日誌檔案,狀態為ACTIVE,就說明其對應的髒資料塊還沒有寫入資料檔案。這時我們可以等待一段時間,等待增量檢查點的完成,也可以手工觸發完全檢查點(complete checkpoint),觸發完全檢查點時,DBWR將把所有髒資料塊寫入磁碟上的資料檔案裡。

SQL> alter system checkpoint;

SQL> select group#,status from v$log;

   GROUP# STATUS

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

        1 INACTIVE

        2 CURRENT

        3 INACTIVE

可以看到,1號日誌檔案的狀態從ACTIVE變成了INACTIVE,說明其對應的髒資料塊都已經寫入了聯機日誌檔案中。

這裡要說明一點。增量檢查點觸發的DBWR寫和完全檢查點觸發的DBWR寫的優先順序是不一樣的,這也就是為何在上面的例子中進行日誌切換以後,前一個日誌的狀態為ACTIVE,而且要等一段時間以後其狀態才能變為INACTIVE,而我們發出alte system checkpoint以後立即變為INACTIVE的原因。因為日誌切換觸發增量檢查點,而增量檢查點通知DBWR啟動,但是由於這時觸發DBWR啟動的條件的優先順序較低,所以DBWR不會立即去寫髒資料塊,而是要等一段時間才會實際的寫髒資料塊。所以我們等待日誌狀態變為INACTIVE的時間就是等DBWR開始真正寫的時間加上DBWR實際寫入資料檔案所花費的時間。而alte system checkpoint觸發完全檢查點,其優先順序很高,所以透過它觸發的DBWR會立即去寫髒資料塊,所以我們等待日誌狀態變為INACTIVE的時間就是DBWR實際寫入資料檔案所花費的時間。

我們可以設定初始化引數:log_checkpoints_to_alerttrue,從而將檢查點啟動和結束的時間記錄到跟蹤檔案裡去。這裡是所記錄的資訊的一個例子:

Wed Dec 13 18:27:48 2006

Beginning log switch checkpoint up to RBA [0x85.2.10], SCN: 2164686

Thread 1 advanced to log sequence 133

 Current log# 3 seq# 133 mem# 0: /oracle/oradata/ora10g/redo03.log

Wed Dec 13 18:32:45 2006

Completed checkpoint up to RBA [0x85.2.10], SCN: 2164686

……

Wed Dec 13 19:02:15 2006

Beginning global checkpoint up to RBA [0x85.883.10], SCN: 2165818

Completed checkpoint up to RBA [0x85.883.10], SCN: 2165818

從上面可以看到,由於日誌切換而發生的增量檢查點從18:27:48開始,到18:32:45結束,用了5分鐘的時間。而我們強制進行完全檢查點,則只用了大概1秒鐘不到的時間。實際上,DBWR在實際寫入資料檔案所花費的時間都是一樣的,也就是不到1秒。5分鐘的差別就在於DBWR等了5分鐘才實際開始寫資料檔案。

在大致瞭解了日誌緩衝區的操作過程以後,我們來深入瞭解一下其內部的工作原理是怎樣的。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/751371/viewspace-567589/,如需轉載,請註明出處,否則將追究法律責任。

相關文章