Oracle Redo 並行機制

zecaro發表於2011-03-21

作者: fuyuncat    來源:   

       Redo log 是用於恢復和一個高階特性的重要資料,一個redo條目包含了相應操作導致的資料庫變化的所有資訊,所有redo條目最終都要被寫入redo檔案中去。Redo log buffer是為了避免Redo檔案IO導致效能瓶頸而在sga中分配出的一塊記憶體。一個redo條目首先在使用者記憶體(PGA)中產生,然後由oracle服務程式複製到log buffer中,當滿足一定條件時,再由LGWR程式寫入redo檔案。由於log buffer是一塊“共享”記憶體,為了避免衝突,它是受到redo allocation latch保護的,每個服務程式需要先獲取到該latch才能分配redo buffer。因此在高併發且資料修改頻繁的oltp系統中,我們通常可以觀察到redo allocation latch的等待。Redo寫入redo buffer的整個過程如下:
 

    在PGA中生產Redo Enrey -> 服務程式獲取Redo Copy latch(存在多個---CPU_COUNT*2) -> 服務程式獲取redo allocation latch(僅1個) -> 分配log buffer -> 釋放redo allocation latch -> 將Redo Entry寫入Log Buffer -> 釋放Redo Copy latch;

shared strand

    為了減少redo allocation latch等待,在oracle 9.2中,引入了log buffer的並行機制。其基本原理就是,將log buffer劃分為多個小的buffer,這些小的buffer被成為strand(為了和之後出現的private strand區別,它們被稱之為shared strand)。每一個strand受到一個單獨redo allocation latch的保護。多個shared strand的出現,使原來序列化的redo buffer分配變成了並行的過程,從而減少了redo allocation latch等待。

    shared strand的初始資料量是由引數log_parallelism控制的;在10g中,該引數成為隱含引數,並新增引數_log_parallelism_max控制shared strand的最大數量;_log_parallelism_dynamic則控制是否允許shared strand數量在_log_parallelism和_log_parallelism_max之間動態變化。
 

SQL程式碼
  1. HELLODBA.COM>select  nam.ksppinm, val.KSPPSTVL, nam.ksppdesc   
  2.   2  from    sys.x$ksppi nam,   
  3.   3          sys.x$ksppsv val   
  4.   4  where nam.indx = val.indx   
  5.   5  --AND   nam.ksppinm LIKE '_%'   
  6.   6  AND   upper(nam.ksppinm) LIKE '%LOG_PARALLE%';   
  7.   
  8. KSPPINM                    KSPPSTVL   KSPPDESC   
  9. -------------------------- ---------- ------------------------------------------   
  10. _log_parallelism           1          Number of log buffer strands   
  11. _log_parallelism_max       2          Maximum number of log buffer strands   
  12. _log_parallelism_dynamic   TRUE       Enable dynamic strands   

    每一個shared strand的大小 = log_buffer/(shared strand數量)。strand資訊可以由表x$kcrfstrand查到(包含shared strand和後面介紹的private strand,10g以後存在)。

SQL程式碼
  1. HELLODBA.COM>select indx,strand_size_kcrfa from x$kcrfstrand where last_buf_kcrfa != '00';   
  2.   
  3.       INDX STRAND_SIZE_KCRFA   
  4. ---------- -----------------   
  5.          0           3514368   
  6.          1           3514368   
  7.   
  8. HELLODBA.COM>show parameter log_buffer   
  9.   
  10. NAME                                 TYPE        VALUE   
  11. ------------------------------------ ----------- ------------------------------   
  12. log_buffer                           integer     7028736  

    關於shared strand的數量設定,16個cpu之內最大預設為2,當系統中存在redo allocation latch等待時,每增加16個cpu可以考慮增加1個strand,最大不應該超過8。並且_log_parallelism_max不允許大於cpu_count。

    注意:在11g中,引數_log_parallelism被取消,shared strand數量由_log_parallelism_max、_log_parallelism_dynamic和cpu_count控制。
 

Private strand

    為了進一步降低redo buffer衝突,在10g中引入了新的strand機制——Private strand。Private strand不是從log buffer中劃分的,而是在shared pool中分配的一塊記憶體空間。
 

SQL程式碼
  1. HELLODBA.COM>select * from V$sgastat where name like '%strand%';   
  2.   
  3. POOL         NAME                            BYTES   
  4. ------------ -------------------------- ----------   
  5. shared pool  private strands               2684928   
  6.   
  7. HELLODBA.COM>select indx,strand_size_kcrfa from x$kcrfstrand where last_buf_kcrfa = '00';   
  8.   
  9.       INDX STRAND_SIZE_KCRFA   
  10. ---------- -----------------   
  11.          2             66560   
  12.          3             66560   
  13.          4             66560   
  14.          5             66560   
  15.          6             66560   
  16.          7             66560   
  17.          8             66560   
  18. ...  

    Private strand的引入為Oracle的Redo/Undo機制帶來很大的變化。每一個Private strand受到一個單獨的redo allocation latch保護,每個Private strand作為“私有的”strand只會服務於一個活動事務。獲取到了Private strand的使用者事務不是在PGA中而是在Private strand生成Redo,當flush private strand或者commit時,Private strand被批次寫入log檔案中。如果新事務申請不到Private strand的redo allocation latch,則會繼續遵循舊的redo buffer機制,申請寫入shared strand中。事務是否使用Private strand,可以由x$ktcxb的欄位ktcxbflg的新增的第13位鑑定:
 

SQL程式碼
  1. HELLODBA.COM>select decode(bitand(ktcxbflg, 4096),0,1,0) used_private_strand, count(*)   
  2.   2    from x$ktcxb   
  3.   3   where bitand(ksspaflg, 1) != 0   
  4.   4     and bitand(ktcxbflg, 2) != 0   
  5.   5   group by bitand(ktcxbflg, 4096);   
  6.   
  7. USED_PRIVATE_STRAND   COUNT(*)   
  8. ------------------- ----------   
  9.                   1         10   
  10.                   0          1  

    對於使用Private strand的事務,無需先申請Redo Copy Latch,也無需申請Shared Strand的redo allocation latch,而是flush或commit是批次寫入磁碟,因此減少了Redo Copy Latch和redo allocation latch申請/釋放次數、也減少了這些latch的等待,從而降低了CPU的負荷。過程如下:
 

    事務開始 -> 申請Private strand的redo allocation latch (申請失敗則申請Shared Strand的redo allocation latch) -> 在Private strand中生產Redo Enrey -> Flush/Commit -> 申請Redo Copy Latch -> 服務程式將Redo Entry批次寫入Log File -> 釋放Redo Copy Latch -> 釋放Private strand的redo allocation latch
 

    注意:對於未能獲取到Private strand的redo allocation latch的事務,在事務結束前,即使已經有其它事務釋放了Private strand,也不會再申請Private strand了。
 

    每個Private strand的大小為65K。10g中,shared pool中的Private strands的大小就是活躍會話數乘以65K,而11g中,在shared pool中需要為每個Private strand額外分配4k的管理空間,即:數量*69k。
 

SQL程式碼
  1. --10g:   
  2. SQL> select * from V$sgastat where name like '%strand%';   
  3.   
  4. POOL         NAME                            BYTES   
  5. ------------ -------------------------- ----------   
  6. shared pool  private strands               1198080   
  7.   
  8. HELLODBA.COM>select trunc(value * KSPPSTVL / 100) * 65 * 1024   
  9.   2    from (select value from v$parameter where name = 'transactions') a,   
  10.   3         (select val.KSPPSTVL   
  11.   4            from sys.x$ksppi nam, sys.x$ksppsv val   
  12.   5           where nam.indx = val.indx   
  13.   6             AND nam.ksppinm = '_log_private_parallelism_mul') b;   
  14.   
  15. TRUNC(VALUE*KSPPSTVL/100)*65*1024   
  16. -------------------------------------   
  17.                               1198080   
  18.   
  19. --11g:   
  20. HELLODBA.COM>select * from V$sgastat where name like '%strand%';   
  21.   
  22. POOL         NAME                            BYTES   
  23. ------------ -------------------------- ----------   
  24. shared pool  private strands                706560   
  25.   
  26. HELLODBA.COM>select trunc(value * KSPPSTVL / 100) * (65 + 4) * 1024   
  27.   2    from (select value from v$parameter where name = 'transactions') a,   
  28.   3         (select val.KSPPSTVL   
  29.   4            from sys.x$ksppi nam, sys.x$ksppsv val   
  30.   5           where nam.indx = val.indx   
  31.   6             AND nam.ksppinm = '_log_private_parallelism_mul') b;   
  32.   
  33. TRUNC(VALUE*KSPPSTVL/100)*(65+4)*1024   
  34. -------------------------------------   
  35.                                706560   

    Private strand的數量受到2個方面的影響:logfile的大小和活躍事務數量。
 

    引數_log_private_mul指定了使用多少logfile空間預分配給Private strand,預設為5。我們可以根據當前logfile的大小(要除去預分配給log buffer的空間)計算出這一約束條件下能夠預分配多少個Private strand:
 

SQL程式碼
  1. HELLODBA.COM>select bytes from v$log where status = 'CURRENT';   
  2.   
  3.      BYTES   
  4. ----------   
  5.   52428800   
  6.   
  7. HELLODBA.COM>select trunc(((select bytes from v$log where status = 'CURRENT') - (select to_number(value) from v$parameter where name = 'log_buffer'))*   
  8.   2         (select to_number(val.KSPPSTVL)   
  9.   3            from sys.x$ksppi nam, sys.x$ksppsv val   
  10.   4           where nam.indx = val.indx   
  11.   5             AND nam.ksppinm = '_log_private_mul') / 100 / 66560)   
  12.   6         as "calculated private strands"  
  13.   7    from dual;   
  14.   
  15. calculated private strands   
  16. --------------------------   
  17.                          5   
  18.   
  19. HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';   
  20.   
  21. actual private strands   
  22. ----------------------   
  23.                      5   

    當logfile切換後(和checkpoint一樣,切換之前必須要將所有Private strand的內容flush到logfile中,因此我們在alert log中可能會發現日誌切換資訊之前會有這樣的資訊:"Private strand flush not complete",這是可以被忽略的),會重新根據切換後的logfile的大小計算對Private strand的限制:
 

SQL程式碼
  1. HELLODBA.COM>alter system switch logfile;   
  2.   
  3. System altered.   
  4.   
  5. HELLODBA.COM>select bytes from v$log where status = 'CURRENT';   
  6.   
  7.      BYTES   
  8. ----------   
  9.  104857600   
  10.   
  11. HELLODBA.COM>select trunc(((select bytes from v$log where status = 'CURRENT') - (select to_number(value) from v$parameter where name = 'log_buffer'))*   
  12.   2         (select to_number(val.KSPPSTVL)   
  13.   3            from sys.x$ksppi nam, sys.x$ksppsv val   
  14.   4           where nam.indx = val.indx   
  15.   5             AND nam.ksppinm = '_log_private_mul') / 100 / 66560)   
  16.   6         as "calculated private strands"  
  17.   7    from dual;   
  18.   
  19. calculated private strands   
  20. --------------------------   
  21.                         13   
  22.   
  23. HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';   
  24.   
  25. actual private strands   
  26. ----------------------   
  27.                     13  

    引數_log_private_parallelism_mul用於推算活躍事務數量在最大事務數量中的百分比,預設為10。Private strand的數量不能大於活躍事務的數量。
 

SQL程式碼
  1. HELLODBA.COM>show parameter transactions   
  2.   
  3. NAME                                 TYPE        VALUE   
  4. ------------------------------------ ----------- ------------------------------   
  5. transactions                         integer     222   
  6. transactions_per_rollback_segment    integer     5   
  7. HELLODBA.COM>select trunc((select to_number(value) from v$parameter where name = 'transactions') *   
  8.   2         (select to_number(val.KSPPSTVL)   
  9.   3            from sys.x$ksppi nam, sys.x$ksppsv val   
  10.   4           where nam.indx = val.indx   
  11.   5             AND nam.ksppinm = '_log_private_parallelism_mul') / 100 )   
  12.   6         as "calculated private strands"  
  13.   7    from dual;   
  14.   
  15. calculated private strands   
  16. --------------------------   
  17.                         22   
  18.   
  19. HELLODBA.COM>select count(1) "actual private strands" from x$kcrfstrand where last_buf_kcrfa = '00';   
  20.   
  21. actual private strands   
  22. ----------------------   
  23.                     22  

    注:在預分配Private strand時,會選擇上述2個條件限制下最小一個數量。但相應的shared pool的記憶體分配和redo allocation latch的數量是按照活躍事務數預分配的。
 

    因此,如果logfile足夠大,_log_private_parallelism_mul與實際活躍程式百分比基本相符的話,Private strand的引入基本可以消除redo allocation latch的爭用問題。

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

相關文章