揭示檢查點的祕密

hanson發表於2019-05-26
檢查點總結
oracle 8以前都是完全檢查點,每次觸發,則記錄當前資料庫SCN號,並將記憶體裡所有截至到該SCN號的髒資料塊都重新整理到資料檔案裡。從而同步資料檔案。

隨著記憶體越來越便宜,以及控制例項恢復的需要,完全檢查點已經不適合實際情況了。於是從oracle 8開始,oracle引入了增量檢查點,通過增量檢查點來減少例項恢復的時間。為此,oracle引入了checkpoint queue,按照資料塊第一次被修改的先後順序,將髒資料塊掛到該佇列上。同時還在該佇列上記錄了每個髒資料塊第一次被修改時產生的redo entry的地址,這叫LRBA。

增量檢查點的啟動時機由以下引數控制:
·fast_start_mttr_target:應用從CKPT position到最後一個redo entry之間所有redo entry所花的時間。也就是例項前滾所花費的時間。
·LOG_CHECKPOINT_INTERVAL:如果自從上一次檢查點啟動以來所累積的日誌塊個數達到該引數,則觸發增量檢查點,從而觸發DBWn寫髒塊。
·LOG_CHECKPOINT_TIMEOUT:兩次檢查點啟動的時間間隔。實際也就是控制髒塊在記憶體裡待多長時間的引數。
·90%*(SUM(log_size) – MAX(log_size)):如果產生的redo塊的量達到該值,則啟動增量檢查點。

只要滿足上面條件中的任何一個,就會啟動增量檢查點。增量檢查點一旦啟動,則會記錄當前CKPT SCN。
然後通知DBWn程式去寫checkpoint queue上所掛載的髒塊,至於寫到哪個髒塊為止,則不超過到當前CKPT SCN。
DBWn程式是批量寫髒塊的,髒塊個數不到一定程度,DBWn不會真的去寫。而每次寫多少個髒塊的個數則由一個隱藏引數控制。

每隔3秒鐘(這叫heartbeat,心跳),“增量檢查點”也還會啟動一次,只不過這時增量檢查點啟動只是檢視一下checkpoint queue,將該佇列上的第一個髒塊所對應的redo block的地址寫入控制檔案,也就是將CKPT Position寫入控制檔案,並不會觸發DBWn程式寫髒塊。所以是打引號的增量檢查點。

有關檢查點更詳細的資訊,google、baidu等都可以搜到,這裡不再贅述。

在10g版本中,完全檢查點也存在,其觸發時機:
1、發出命令:alter system checkpoint;
2、正常關閉資料庫。

有關檢查點(包括完全檢查點和增量檢查點)的檢視主要有兩個:
1、v$instance_recovery。其中主要的欄位包括:
·actual_redo_blks:最近一次檢查點所對應的要寫入的髒塊所對應的redo block到寫入了日誌檔案的最後一個redo block之間的redo blocks數量;
·target_redo_blks:所有檢查點條件中最小的條件所導致的在記憶體裡的redo blocks數量;
·log_file_size_redo_blks:其欄位值為90%*(SUM(log_size) – MAX(log_size))的計算結果。
·log_chkpt_timeout_redo_blks:由log_checkpoint_timeout引數所得到的redo blocks數量。
·target_mttr:由fast_start_mttr_target引數所計算出來的例項恢復所需要的時間。
·estimated_mttr:根據當前最後一次檢查點與日誌尾所差的redo blocks數量估算出來的例項恢復所需要的時間。

2、X$KCCCP。該記憶體表的命名含義為:
[K]ernel [C]ache [C]ontrolfile management [c]heckpoint [p]rogress。
其中主要的欄位包括:
·CPLRBA_SEQ:最後一次增量檢查點對應LRBA的第一部分--日誌序列號;
·CPLRBA_BNO:最後一次增量檢查點對應LRBA的第二部分--日誌塊數;
·CPLRBA_BOF:最後一次增量檢查點對應LRBA的第三部分--日誌偏移量;
·CPODR_SEQ:已寫入日誌檔案的最後一個RBA的第一部分--日誌序列號;
·CPODR_BNO:已寫入日誌檔案的最後一個RBA的第二部分--日誌塊數;
·CPODR_BOF:已寫入日誌檔案的最後一個RBA的第二部分--日誌偏移量;
·CPHBT:每三秒更新一次,並寫入控制檔案的heartbeat部分。表示這時的SCN號。
換句話所,當進行例項恢復時,需要應用的redo entry從CPLRBA_*到CPODR_*為止。

一、完全檢查點
SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        37      97550         37      97778

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
        63

SQL> alter system checkpoint;

系統已更改。

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
         0

很明顯,發出該命令以後,記憶體裡所有髒塊都被寫入了資料檔案,於是記憶體裡就沒有髒塊了。
開啟跟蹤檔案,可以看到:
Beginning global checkpoint up to RBA [0x25.17df2.10], SCN: 662364
Completed checkpoint up to RBA [0x25.17df2.10], SCN: 662364

SQL> select to_number('25','xx') from dual;

TO_NUMBER('25','XX')
--------------------
                  37

SQL> select to_number('17df2','xxxxxxxx') from dual;

TO_NUMBER('17DF2','XXXXXXXX')
-----------------------------
                        97778

將其與x$kcccp的查詢結果比較一下,也可以看到,在完全檢查點中,截至寫入資料檔案的髒塊所對應的redo entry對應到STOP_SEQ#和STOP_BLK#所指明的redo entry為止。

二、增量檢查點
測試增量檢查點,主要通過v$instance_recovery檢視。
我們通常不會去修改log_checkpoint和fast_start引數,那些引數的預設值都設定的很大,所以還沒到那些引數所指定的上限的時候,90%這個條件就生效了。因為增量檢查點在任何一個條件滿足就會被觸發。
按照前面的公式:90%*(SUM(log_size) – MAX(log_size)),我們來計算日誌量是多大。
SQL> select 0.90*(sum(bytes)/1024/1024-max(bytes/1024/1024)) from v$log;

0.90*(SUM(BYTES)/1024/1024-MAX(BYTES/1024/1024))
------------------------------------------------
                                             180
可以看到,只要日誌量達到180M,就會發生增量檢查點。然後來驗證:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
              89           368640     368640                        0             27
這裡368640表示redo block的個數,至於每個redo block多大,則通常為500 bytes。也可以通過下面的sql來驗證:
SQL> select distinct lebsz as redo_block_size from x$kccle where lebsz>0;

REDO_BLOCK_SIZE
---------------
            512

於是,我們得到:368640*512/1024/1024=180M。很明顯,與前面的公式吻合。
同時,我們看到當前記憶體裡的髒塊所對應的redo block有89個。而其他控制增量檢查點的引數都保持預設值:
SQL> show parameter log_checkpoint

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
log_checkpoint_interval              integer     0
log_checkpoint_timeout               integer     1800
log_checkpoints_to_alert             boolean     TRUE
SQL> show parameter fast_start

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
fast_start_io_target                 integer     0
fast_start_mttr_target               integer     0
fast_start_parallel_rollback         string      LOW

假設當前所在的session為sess #1。然後再啟動一個session,假設其session為sess #2。發出大量的DML語句。
SQL> connect hr/hr
已連線。
SQL> create table test as select * from dba_objects;

表已建立。

回到sess #1,檢視v$instance_recovery:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
             308           368640     368640                        0             22
可以看到,只產生了大約300多個redo block。於是回到sess #2,不斷的做insert+delete:
SQL> insert into test select * from test;

已建立50320行。

SQL> commit;

提交完成。

SQL> delete test;

已刪除100640行。

SQL> commit;

提交完成。

大概做3、4次左右,回到sess #1,檢視v$instance_recovery。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
          341604           368640     368640                        0            105

這時記憶體裡的髒資料塊所對應的redo block已經接近上限了。於是再次回到sess #2裡做DML,結束以後回到sess #1檢視。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
          378157           368640     368640                        0            184
已經超過上限,於是觸發增量檢查點。這時可以去檢視alert log:
Sun Nov 23 12:47:47 2008
Beginning log switch checkpoint up to RBA [0x26.2.10], SCN: 667801
Thread 1 advanced to log sequence 38
  Current log# 6 seq# 38 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO06.LOG
Sun Nov 23 12:48:47 2008
Beginning log switch checkpoint up to RBA [0x27.2.10], SCN: 677714
Thread 1 advanced to log sequence 39
  Current log# 4 seq# 39 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO04.LOG
很明顯,可以看到觸發了2次增量檢查點,第一次增量檢查點要寫到[0x26.2.10]為止,第二次要寫到[0x27.2.10]為止。同時這兩次增量檢查點都還沒有完成,因為在checkpoint queue上,截至到該RBA的redo entry所對應的髒塊還沒有被寫入資料檔案。增量檢查點只是通知DBWn寫到這個位置並不等DBWn寫完就返回了。這裡的26對應到十進位制就是:2*16+6=38,也就是寫到序列號為38為止。而27則對應到39號日誌檔案。
SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        37     108095         39      85497

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

    GROUP#  SEQUENCE# STATUS
---------- ---------- ----------------
         4         39 CURRENT
         5         37 ACTIVE
         6         38 ACTIVE

可以看到37、38這兩個日誌所對應的髒塊還沒有被寫入資料檔案,所以其狀態為active。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
          368599           368640     368640                        0            161

同時也發現,記憶體裡的髒塊所對應的redo block個數從378157下降到了368599,也就是隻減少了很少的redo block,換句花說,這時DBWn程式只寫了很少的幾個髒塊。因為寫的髒塊個數只需要滿足不觸發增量檢查點即可,這裡的條件也就是記憶體裡的髒塊所對應的redo block不要超過368640即可。

然後再次的在sess #2裡做DML。然後回到sess #1。
SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        38      22297         39     195257
可以看到發生變化了,checkpoint position從37號日誌向前推進到了38號日誌。也就是說,完成了一次檢查點。
馬上去看alert log,發現下面這一段:
Thread 1 advanced to log sequence 39
  Current log# 4 seq# 39 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO04.LOG
Sun Nov 23 13:00:56 2008
Completed checkpoint up to RBA [0x26.2.10], SCN: 667801
Sun Nov 23 13:01:57 2008
Beginning log switch checkpoint up to RBA [0x28.2.10], SCN: 685552
Thread 1 advanced to log sequence 40
  Current log# 5 seq# 40 mem# 0: C:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\REDO05.LOG
很明顯,第一次增量檢查點結束了,也就是說,前面active的37號日誌檔案裡的redo entry所對應的髒塊已經被
寫入了資料檔案,其狀態肯定變為inactive。同時第二次增量檢查點還沒有結束,同時又啟動了第三次增量檢查點。
SQL> select group#,sequence#,status from v$log;

    GROUP#  SEQUENCE# STATUS
---------- ---------- ----------------
         4         39 CURRENT
         5         37 INACTIVE
         6         38 ACTIVE

SQL> select     CPLRBA_SEQ as cur_seq#,
  2     CPLRBA_BNO as cur_blk#,
  3     CPODR_SEQ as stop_seq#,
  4     CPODR_BNO as stop_blk#
  5  from x$kcccp
  6  where      CPODR_SEQ>0;

  CUR_SEQ#   CUR_BLK#  STOP_SEQ#  STOP_BLK#
---------- ---------- ---------- ----------
        38      22297         39     195257

整個過程是很容易理解的。這裡可以很容易得出結論:通常所說的logfile switch 觸發檢查點,實際上就是觸發增量檢查點而不是完全檢查點,並標記要寫到哪個髒塊為止。同時不等DBWn寫完這些髒塊就返回。當DBWn寫完所標記的髒塊以後,該增量檢查點結束。

然後來修改log_checkpoint_timeout引數,從而調整增量檢查點的頻率。該參數列示髒資料塊留在記憶體裡的時間長度,一旦超過該時間,就會觸發增量檢查點,從而確定要寫到的redo entry,從而確定了要寫到的髒塊地址,然後觸發DBWn程式寫髒塊。將該引數設定為60秒。
SQL> alter system set log_checkpoint_timeout=60;

系統已更改。

SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
               6               40     368640           40           0             23

可以看到,修改為60秒以後,TARGET_REDO_BLKS為40,表示記憶體裡只能容納40個redo block所保護的髒資料塊。然後在sess #2裡進行DML以後,回到sess #1裡檢視相關檢視。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
           22791            22791     368640        22791           0             25
等待1分鐘左右,再次查詢該檢視,會發現其結果為:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
              73               75     368640           75           0             22
很明顯,1分鐘以後,大部分髒塊被增量檢查點觸發DBWn寫入資料檔案。

然後來測試fast_start_mttr_target引數。
SQL> alter system set fast_start_mttr_target=40;

系統已更改。

SQL> show parameter log_checkpoint

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
log_checkpoint_interval              integer     0
log_checkpoint_timeout               integer     0
log_checkpoints_to_alert             boolean     TRUE
SQL> show parameter fast_start

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
fast_start_io_target                 integer     0
fast_start_mttr_target               integer     40
fast_start_parallel_rollback         string      LOW

設定完fast_start_mttr_target=40以後,立刻檢視相關檢視:
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
             104           368640     368640                       40             22

可以看到,TARGET_MTTR就是我們設定的40秒,而ESTIMATED_MTTR則表示由於當前記憶體裡髒塊很少,因此如果當前例項崩潰,則只需要22秒就能完成例項的恢復。

在sess #2裡進行DML以後,馬上到sess #1裡檢視相關檢視。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
           67141           368640     368640                       40             42

可以看到這時sess #2產生了67141個redo block,這將觸發增量檢查點。然後我們間隔很短的時間,比如30秒以後再次查詢該檢視。
SQL> select     actual_redo_blks,
  2     target_redo_blks,
  3     LOG_FILE_SIZE_REDO_BLKS as "90%_blks",
  4     LOG_CHKPT_TIMEOUT_REDO_BLKS as timeout_blks,
  5     target_mttr,
  6          estimated_mttr
  7  from v$instance_recovery;

ACTUAL_REDO_BLKS TARGET_REDO_BLKS   90%_blks TIMEOUT_BLKS TARGET_MTTR ESTIMATED_MTTR
---------------- ---------------- ---------- ------------ ----------- --------------
             132           368640     368640                       40             42

會發現,由於我們設定了40秒的例項恢復時間,因此馬上就完成了增量檢查點,減少了記憶體裡髒資料塊的個數。

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

相關文章