1. 程式人生 > >Oracle 記憶體分析

Oracle 記憶體分析

Oracle的記憶體配置與oracle效能息息相關。而且關於記憶體的錯誤(如4030、4031錯誤)都是十分令人頭疼的問題。可以說,關於記憶體的配置,是最影響Oracle效能的配置。記憶體還直接影響到其他兩個重要資源的消耗:CPU和IO。

首先,看看Oracle記憶體儲存的主要內容是什麼:

  • 程式程式碼(PLSQL、Java);
  • 關於已經連線的會話的資訊,包括當前所有活動和非活動會話;
  • 程式執行時必須的相關資訊,例如查詢計劃;
  • Oracle程序之間共享的資訊和相互交流的資訊,例如鎖;
  • 那些被永久儲存在外圍儲存介質上,被cache在記憶體中的資料(如redo log條目,資料塊)。

此外,需要記住的一點是,Oracle的記憶體是與例項對應的。也就是說,一個例項就有一個獨立的記憶體結構。

先從Oracle記憶體的組成架構介紹。

1.   Oracle的記憶體架構組成

Oracle的記憶體,從總體上講,可以分為兩大塊:共享部分(主要是SGA)和程序獨享部分(主要是PGA和UGA)。而這兩部分記憶體裡面,根據功能不同,還分為不同記憶體池(Pool)和記憶體區(Area)。下面就是Oracle記憶體構成框架圖:

SGA

Share Pool

Buffer Cache

Redo Log Buffer

Java Pool

Stream Pool(10g)

Large Pool

PGA*n

Bitmap merge area

Sort Area

Hash Area

UGA*n

CUA*n

下面分別介紹這兩塊記憶體區。

1.1.            SGA(System Global Area)

SGA(System Global Area 系統全域性區域)是一組包含一個Oracle例項的資料和控制資訊的共享記憶體結構。這句話可以說是SGA的定義。雖然簡單,但其中闡述了SGA幾個很重要的特性:1、SGA的構成——資料和控制資訊,我們下面會詳細介紹;2、SGA是共享的,即當有多個使用者同時登入了這個例項,SGA中的資訊可以被它們同時訪問(當涉及到互斥的問題時,由latch和enquence控制);3、一個SGA只服務於一個例項,也就是說,當一臺機器上有多個例項執行時,每個例項都有一個自己的SGA,儘管SGA來自於OS的共享記憶體區,但例項之間不能相互訪問對方的SGA區。

Oracle程序和一個SGA就構成了一個Oracle例項。當例項啟動時,Oracle會自動從系統中分配記憶體給SGA,而例項關閉時,作業系統會回收這些記憶體。下面就是當例項啟動後,顯示已經分配了SGA:

SQL> startup
ORACLE instance started.
Total System Global Area  289406976 bytes
Fixed Size                  1248576 bytes
Variable Size             117441216 bytes
Database Buffers          163577856 bytes
Redo Buffers                7139328 bytes
Database mounted.
Database opened.
SQL>

SGA區是可讀寫的。所有登入到例項的使用者都能讀取SGA中的資訊,而在oracle做執行操作時,服務程序會將修改的資訊寫入SGA區。

SGA主要包括了以下的資料結構:

  • 資料緩衝(Buffer Cache)
  • 重做日誌緩衝(Redo Log Buffer)
  • 共享池(Shared Pool)
  • Java池(Java Pool)
  • 大池(Large Pool)
  • 流池(Streams Pool --- 10g以後才有)
  • 資料字典快取(Data Dictionary Cache)
  • 其他資訊(如資料庫和例項的狀態資訊)

最後的兩種記憶體資訊會被例項的後臺程序所訪問,它們在例項啟動後就固定在SGA中了,而且不會改變,所以這部分又稱為固定SGA(Fixed SGA)。這部分割槽域的大小一般小於100K。

此外,用於並非程序控制的鎖(latch)的資訊也包含在SGA區中。

Shared Pool、Java Pool、Large Pool和Streams Pool這幾塊記憶體區的大小是相應系統引數設定而改變的,所以有通稱為可變SGA(Variable SGA)。

1.1.1.   SGA的重要引數和特性

在設定SGA時,有一些很重要的引數,它們設定正確與否,會直接影響到系統的整體效能。下面一一介紹他們:

·        SGA_MAX_SIZE

SGA區包括了各種緩衝區和記憶體池,而大部分都可以通過特定的引數來指定他們的大小。但是,作為一個昂貴的資源,一個系統的實體記憶體大小是有限。儘管對於CPU的記憶體定址來說,是無需關係實際的實體記憶體大小的(關於這一點,後面會做詳細的介紹),但是過多的使用虛擬記憶體導致page in/out,會大大影響系統的效能,甚至可能會導致系統crash。所以需要有一個引數來控制SGA使用虛擬記憶體的最大大小,這個引數就是SGA_MAX_SIZE。

當例項啟動後,各個記憶體區只分配例項所需要的最小大小,在隨後的執行過程中,再根據需要擴充套件他們的大小,而他們的總和大小受到了SGA_MAX_SIZE的限制。

當試圖增加一個記憶體的大小,並且如果這個值導致所有記憶體區大小總和大於SGA_MAX_SIZE時,oracle會提示錯誤,不允許修改。

當然,如果在設定引數時,指定區域為spfile時(包括修改SGA_MAX_SIZE本身),是不會受到這個限制的。這樣就可能出現這樣的情況,在spfile中,SGA各個記憶體區設定大小總和大於SGA_MAX_SIZE。這時,oracle會如下處理:當例項再次啟動時,如果發現SGA各個記憶體總和大於SGA_MAX_SIZE,它會將SGA_MAX_SIZE的值修改為SGA各個記憶體區總和的值。

SGA所分配的是虛擬記憶體,但是,在我們配置SGA時,一定要使整個SGA區都在實體記憶體中,否則,會導致SGA頻繁的頁入/頁出,會極大影響系統性能。

對於OLTP系統,我個人建議可以如下配置SGA_MAX_SIZE(一般有經驗的DBA都會有自己的預設配置大小,你也可以通過一段時間的觀察、調整自己的系統來得到適合本系統的引數配置):

系統記憶體

SGA_MAX_SIZE值

1G

400-500M

2G

1G

4G

2500M

8G

5G

SGA的實際大小可以通過以下公式估算:

SGA實際大小 = DB_CACHE_SIZE + DB_KEEP_CACHE_SIZE + DB_RECYCLE_CACHE_SIZE + DB_nk_CACHE_SIZE + SHARED_POOL_SIZE + LARGE_POOL_SIZE + JAVA_POOL_SIZE + STREAMS_POOL_SIZE(10g中的新記憶體池) + LOG_BUFFERS+11K(Redo Log Buffer的保護頁) + 1MB + 16M(SGA內部記憶體消耗,適合於9i及之前版本)

公式種涉及到的引數在下面的內容種會一一介紹。

·        PRE_PAGE_SGA

我們前面提到,oracle例項啟動時,會只載入各個記憶體區最小的大小。而其他SGA記憶體只作為虛擬記憶體分配,只有當程序touch到相應的頁時,才會置換到實體記憶體中。但我們也許希望例項一啟動後,所有SGA都分配到實體記憶體。這時就可以通過設定PRE_PAGE_SGA引數來達到目的了。

這個引數的預設值為FALSE,即不將全部SGA置入實體記憶體中。當設定為TRUE時,例項啟動會將全部SGA置入實體記憶體中。它可以使例項啟動達到它的最大效能狀態,但是,啟動時間也會更長(因為為了使所有SGA都置入實體記憶體中,oracle程序需要touch所有的SGA頁)。

我們可以通過TopShow工具(本站原創工具,可在http://www.HelloDBA.com/Download/TopShow.html中下載)來觀察windows(Unix下的記憶體監控比較複雜,這裡暫不舉例)下引數修改前後的對比。

PRE_PAGE_SGA為FALSE:

SQL> show parameter sga
NAME                                 TYPE        VALUE
------------------------------------ ----------- --------------------------
lock_sga                             boolean     FALSE
pre_page_sga                         boolean     FALSE
sga_max_size                         big integer 276M
sga_target                           big integer 276M
SQL> startup force
ORACLE instance started.
Total System Global Area  289406976 bytes
Fixed Size                  1248576 bytes
Variable Size             117441216 bytes
Database Buffers          163577856 bytes
Redo Buffers                7139328 bytes
Database mounted.
Database opened.
SQL>

啟動後,Oracle的記憶體情況

可以看到,例項啟動後,oracle佔用的實體記憶體只有168M,遠小於SGA的最大值288M(實際上,這部分實體記憶體中還有一部分程序的PGA和Oracle Service佔用的記憶體),而虛擬記憶體則為340M。

將PRE_PAGE_SGA修改為TRUE,重啟例項:

SQL> alter system set pre_page_sga=true scope=spfile;
System altered.
SQL> startup force
ORACLE instance started.
Total System Global Area  289406976 bytes
Fixed Size                  1248576 bytes
Variable Size             117441216 bytes
Database Buffers          163577856 bytes
Redo Buffers                7139328 bytes
Database mounted.
Database opened.

再觀察啟動後Oracle的記憶體分配情況:

這時看到,例項啟動後實體記憶體達到了最大343M,於虛擬記憶體相當。這時,oracle例項已經將所有SGA分配到實體記憶體。

當引數設定為TRUE時,不僅在例項啟動時,需要touch所有的SGA頁,並且由於每個oracle程序都會訪問SGA區,所以每當一個新程序啟動時(在Dedicated Server方式中,每個會話都會啟動一個Oracle程序),都會touch一遍該程序需要訪問的所有頁。因此,每個程序的啟動時間頁增長了。所以,這個引數的設定需要根據系統的應用情況來設定。

在這種情況下,程序啟動時間的長短就由系統記憶體的頁的大小來決定了。例如,SGA大小為100M,當頁的大小為4K時,程序啟動時需要訪問100000/4=25000個頁,而如果頁大小為4M時,程序只需要訪問100/4=25個頁。頁的大小是由作業系統指定的,並且是無法修改的。

但是,要記住一點:PRE_PAGA_SGA只是在啟動時將實體記憶體分配給SGA,但並不能保證系統在以後的執行過程不會將SGA中的某些頁置換到虛擬記憶體中,也就是說,儘管設定了這個引數,還是可能出現Page In/Out。如果需要保障SGA不被換出,就需要由另外一個引數LOCK_SGA來控制了。

·        LOCK_SGA

上面提到,為了保證SGA都被鎖定在實體記憶體中,而不必頁入/頁出,可以通過引數LOCK_SGA來控制。這個引數預設值為FALSE,當指定為TRUE時,可以將全部SGA都鎖定在實體記憶體中。當然,有些系統不支援記憶體鎖定,這個引數也就無效了。

·        SGA_TARGET

這裡要介紹的時Oracle10g中引入的一個非常重要的引數。在10g之前,SGA的各個記憶體區的大小都需要通過各自的引數指定,並且都無法超過引數指定大小的值,儘管他們之和可能並沒有達到SGA的最大限制。此外,一旦分配後,各個區的記憶體只能給本區使用,相互之間是不能共享的。拿SGA中兩個最重要的記憶體區Buffer Cache和Shared Pool來說,它們兩個對例項的效能影響最大,但是就有這樣的矛盾存在:在記憶體資源有限的情況下,某些時候資料被cache的需求非常大,為了提高buffer hit,就需要增加Buffer Cache,但由於SGA有限,只能從其他區“搶”過來——如縮小Shared Pool,增加Buffer Cache;而有時又有大塊的PLSQL程式碼被解析駐入記憶體中,導致Shared Pool不足,甚至出現4031錯誤,又需要擴大Shared Pool,這時可能又需要人為干預,從Buffer Cache中將記憶體奪回來。

有了這個新的特性後,SGA中的這種記憶體矛盾就迎刃而解了。這一特性被稱為自動共享記憶體管理(Automatic Shared Memory Management ASMM)。而控制這一特性的,也就僅僅是這一個引數SGA_TARGE。設定這個引數後,你就不需要為每個記憶體區來指定大小了。SGA_TARGET指定了SGA可以使用的最大記憶體大小,而SGA中各個記憶體的大小由Oracle自行控制,不需要人為指定。Oracle可以隨時調節各個區域的大小,使之達到系統性能最佳狀態的個最合理大小,並且控制他們之和在SGA_TARGET指定的值之內。一旦給SGA_TARGET指定值後(預設為0,即沒有啟動ASMM),就自動啟動了ASMM特性。

設定了SGA_TARGET後,以下的SGA記憶體區就可以由ASMM來自動調整:

  • 共享池(Shared Pool)
  • Java池(Java Pool)
  • 大池(Large Pool)
  • 資料快取區(Buffer Cache)
  • 流池(Streams Pool)

對於SGA_TARGET的限制,它的大小是不能超過SGA_MAX_SIZE的大小的。

SQL> show parameter sga
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
lock_sga                             boolean     FALSE
pre_page_sga                         boolean     FALSE
sga_max_size                         big integer 276M
sga_target                           big integer 276M
SQL>
SQL>
SQL>
SQL> alter system set sga_target=280M;
alter system set sga_target=280M
*
ERROR at line 1:
ORA-02097: parameter cannot be modified because specified value is invalid
ORA-00823: Specified value of sga_target greater than sga_max_size

另外,當指定SGA_TARGET小於SGA_MAX_SIZE,例項重啟後,SGA_MAX_SIZE就自動變為和SGA_TARGET一樣的值了。

SQL> show parameter sga
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
lock_sga                             boolean     FALSE
pre_page_sga                         boolean     FALSE
sga_max_size                         big integer 276M
sga_target                           big integer 276M
SQL> alter system set sga_target=252M;
System altered.
SQL> show parameter sga
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
lock_sga                             boolean     FALSE
pre_page_sga                         boolean     FALSE
sga_max_size                         big integer 276M
sga_target                           big integer 252M
SQL> startup force
ORACLE instance started.
Total System Global Area  264241152 bytes
Fixed Size                  1248428 bytes
Variable Size             117441364 bytes
Database Buffers          138412032 bytes
Redo Buffers                7139328 bytes
Database mounted.
Database opened.
SQL> show parameter sga
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
lock_sga                             boolean     FALSE
pre_page_sga                         boolean     FALSE
sga_max_size                         big integer 252M
sga_target                           big integer 252M
SQL>

對於SGA_TARGET,還有重要一點就是,它的值可以動態修改(在SGA_MAX_SIZE範圍內)。在10g之前,如果需要修改SGA的大小(即修改SGA_MAX_SIZE的值)需要重啟例項才能生效。當然,在10g中,修改SGA_MAX_SIZE的值還是需要重啟的。但是有了SGA_TARGET後,可以將SGA_MAX_SIZE設定偏大,再根據實際需要調整SGA_TARGET的值(我個人不推薦頻繁修改SGA的大小,SGA_TARGET在例項啟動時設定好,以後不要再修改)。

SGA_TARGET帶來一個重要的好處就是,能使SGA的利用率達到最佳,從而節省記憶體成本。因為ASMM啟動後,Oracle會自動根據需要調整各個區域的大小,大大減少了某些區域記憶體緊張,而某些區域又有記憶體空閒的矛盾情況出現。這也同時大大降低了出現4031錯誤的機率。

·        use_indirect_data_buffers

這個引數使32位平臺使用擴充套件緩衝快取基址,以支援支援4GB多實體記憶體。設定此引數,可以使SGA突破在32位系統中的2G最大限制。64位平臺中,這個引數被忽略。

二.

1.1.2.   關於SGA的重要檢視

要了解和觀察SGA的使用情況,並且根據統計資料來處理問題和調整效能,主要有以下的幾個系統檢視。

·        v$sga

這個檢視包括了SGA的的總體情況,只包含兩個欄位:name(SGA記憶體區名字)和value(記憶體區的值,單位為位元組)。它的結果和show sga的結果一致,顯示了SGA各個區的大小:

SQL> select * from v$sga;
NAME                      VALUE
-------------------- ----------
Fixed Size              1248428
Variable Size         117441364
Database Buffers      138412032
Redo Buffers            7139328
4 rows selected.
SQL> show sga
Total System Global Area  264241152 bytes
Fixed Size                  1248428 bytes
Variable Size             117441364 bytes
Database Buffers          138412032 bytes
Redo Buffers                7139328 bytes
SQL>

·        v$sgastat

這個檢視比較重要。它記錄了關於sga的統計資訊。包含三個欄位:Name(SGA記憶體區的名字);Bytes(記憶體區的大小,單位為位元組);Pool(這段記憶體所屬的記憶體池)。

這個檢視尤其重要的是,它詳細記錄了個各個池(Pool)記憶體分配情況,對於定位4031錯誤有重要參考價值。

以下語句可以查詢Shared Pool空閒率:

SQL> select to_number(v$parameter.value) value, v$sgastat.BYTES,
  2         (v$sgastat.bytes/v$parameter.value)*100 "percent free"
  3      from v$sgastat, v$parameter
  4      where v$sgastat.name= 'free memory'
  5      and v$parameter.name = 'shared_pool_size'
  6      and v$sgastat.pool='shared pool'
  7  ;
     VALUE      BYTES percent free
---------- ---------- ------------
 503316480  141096368 28.033329645
SQL>

·        v$sga_dynamic_components

這個檢視記錄了SGA各個動態記憶體區的情況,它的統計資訊是基於已經完成了的,針對SGA動態記憶體區大小調整的操作,欄位組成如下:

欄位

資料型別

描述

COMPONENT

VARCHAR2(64)

記憶體區名稱

CURRENT_SIZE

NUMBER

當前大小

MIN_SIZE

NUMBER

自從例項啟動後的最小值

MAX_SIZE

NUMBER

自從例項啟動後的最大值

OPER_COUNT

NUMBER

自從例項啟動後的調整次數

LAST_OPER_TYPE

VARCHAR2(6)

最後一次完成的調整動作,值包括:

  • GROW(增加)
  • SHRINK(縮小)

LAST_OPER_MODE

VARCHAR2(6)

最後一次完成的調整動作的模式,包括:

  • MANUAL(手動)
  • AUTO(自動)

LAST_OPER_TIME

DATE

最後一次完成的調整動作的開始時間

GRANULE_SIZE

NUMBER

GRANULE大小(關於granule後面詳細介紹)

·        V$SGA_DYNAMIC_FREE_MEMORY

這個檢視只有一個欄位,一條記錄:當前SGA可用於動態調整SGA記憶體區的空閒區域大小。它的值相當於(SGA_MAX_SIZE – SGA各個區域設定大小的總和)。當設定了SGA_TARGET後,它的值一定為0(為什麼就不需要我再講了吧^_^)。

下面的例子可以很清楚的看到這個檢視的作用:

SQL> select * from v$sga_dynamic_free_memory;
CURRENT_SIZE
--------------
0
SQL> show parameter shared_pool
NAME                                TYPE        VALUE
----------------------------------- ----------- ----------
shared_pool_size                    big integer 50331648
SQL> alter system set shared_pool_size=38M;
system altered.
SQL> show parameter shared_pool
NAME                                TYPE        VALUE
----------------------------------- ----------- ----------
shared_pool_size                    big integer 41943040
SQL> select * from v$sga_dynamic_free_memory;
CURRENT_SIZE
--------------
8388608

1.1.3.   資料庫緩衝區(Database Buffers)

Buffer Cache是SGA區中專門用於存放從資料檔案中讀取的的資料塊拷貝的區域。Oracle程序如果發現需要訪問的資料塊已經在buffer cache中,就直接讀寫記憶體中的相應區域,而無需讀取資料檔案,從而大大提高效能(要知道,記憶體的讀取效率是磁碟讀取效率的14000倍)。Buffer cache對於所有oracle程序都是共享的,即能被所有oracle程序訪問。

和Shared Pool一樣,buffer cache被分為多個集合,這樣能夠大大降低多CPU系統中的爭用問題。

1.1.3.1.    Buffer cache的管理

Oracle對於buffer cache的管理,是通過兩個重要的連結串列實現的:寫連結串列和最近最少使用連結串列(the Least Recently Used LRU)。寫連結串列所指向的是所有髒資料塊快取(即被程序修改過,但還沒有被回寫到資料檔案中去的資料塊,此時緩衝中的資料和資料檔案中的資料不一致)。而LRU連結串列指向的是所有空閒的快取、pin住的快取以及還沒有來的及移入寫連結串列的髒快取。空閒快取中沒有任何有用的資料,隨時可以使用。而pin住的快取是當前正在被訪問的快取。LRU連結串列的兩端就分別叫做最近使用端(the Most Recently Used MRU)和最近最少使用端(LRU)。

·        Buffer cache的資料塊訪問

當一個Oracle程序訪問一個快取是,這個程序會將這塊快取移到LRU連結串列中的MRU。而當越來越多的緩衝塊被移到MRU端,那些已經過時的髒緩衝(即資料改動已經被寫入資料檔案中,此時緩衝中的資料和資料檔案中的資料已經一致)則被移到LRU連結串列中LRU端。

當一個Oracle使用者程序第一次訪問一個數據塊時,它會先查詢buffer cache中是否存在這個資料塊的拷貝。如果發現這個資料塊已經存在於buffer cache(即命中cache hit),它就直接讀從記憶體中取該資料塊。如果在buffer cache中沒有發現該資料塊(即未命中cache miss),它就需要先從資料檔案中讀取該資料塊到buffer cache中,然後才訪問該資料塊。命中次數與程序讀取次數之比就是我們一個衡量資料庫效能的重要指標:buffer hit ratio(buffer命中率),可以通過以下語句獲得自例項啟動至今的buffer命中率:

SQL> select 1-(sum(decode(name, 'physical reads', value, 0))/
  2           (sum(decode(name, 'db block gets', value, 0))+
  3           (sum(decode(name, 'consistent gets', value, 0))))) "Buffer Hit Ratio"
  4  from v$sysstat;
Buffer Hit Ratio
----------------
      .926185625
1 row selected.
SQL>

根據經驗,一個良好效能的系統,這一值一般保持在95%左右。

上面提到,如果未命中(missed),則需要先將資料塊讀取到快取中去。這時,oracle程序需要從空閒列表種找到一個適合大小的空閒快取。如果空閒列表中沒有適合大小的空閒buffer,它就會從LRU端開始查詢LRU連結串列,直到找到一個可重用的快取塊或者達到最大查詢塊數限制。在查詢過程中,如果程序找到一個髒快取塊,它將這個快取塊移到寫連結串列中去,然後繼續查詢。當它找到一個空閒塊後,就從磁碟中讀取資料塊到快取塊中,並將這個快取塊移到LRU連結串列的MRU端。

當有新的物件需要請求分配buffer時,會通過記憶體管理模組請求分配空閒的或者可重用的buffer。“free buffer requested”就是產生這種請求的次數;

當請求分配buffer時,已經沒有適合大小的空閒buffer時,需要從LRU連結串列上獲取到可重用的buffer。但是,LRU連結串列上的buffer並非都是立即可重用的,還會存在一些塊正在被讀寫或者已經被別的使用者所等待。根據LRU演算法,查詢可重用的buffer是從連結串列的LRU端開始查詢的,如果這一段的前面存在這種不能理解被重用的buffer,則需要跳過去,查詢連結串列中的下一個buffer。“free buffer inspected”就是被跳過去的buffer的數目。

如果Oracle使用者程序達到查詢塊數限制後還沒有找到空閒快取,它就停止查詢LRU連結串列,並且通過訊號同志DBW0程序將髒快取寫入磁碟去。

下面就是oracle使用者程序訪問一個數據塊的虛擬碼:

user_process_access_block(block)
{
    if (search_lru(block))
    {
        g_cache_hit++;
        return read_block_from_buffer_cache(block);
    }
    else
    {
        g_cache_missed++;
        search_count = 1;
        searched = FALSE;
        set_lru_latch_context();
        buffer_block = get_lru_from_lru();
        do
        {
            if (block == buffer_block)
            {
                set_buffer_block(buffer_block, read_block_from_datafile(block);
                move_buffer_block_to_mru(buffer_block);
                searched = TRUE;
            }
            search_count++;
            buffer_block = get_next_from_lru(buffer_block);
        }while(!searched && search_count < BUFFER_SEARCH_THRESHOLD)
        free_lru_latch_context();
        if (!searched)
        {
            buffer_block = signal_dbw0_write_dirty_buffer();
            set_buffer_block(buffer_block, read_block_from_datafile(block);
            move_buffer_block_to_mru(buffer_block);
        }
        return buffer_block;
    }
}
·        全表掃描

當發生全表掃描(Full Table Scan)時,使用者程序讀取表的資料塊,並將他們放在LRU連結串列的LRU端(和上面不同,不是放在MRU端)。這樣做的目的是為了使全表掃描的資料儘快被移出。因為全表掃描一般發生的頻率較低,並且全表掃描的資料塊大部分在以後都不會被經常使用到。

而如果你希望全表掃描的資料能被cache住,使之在掃描時放在MRU端,可以通過在建立或修改表(或簇)時,指定CACHE引數。

·        Flush Buffer

回顧一下前面一個使用者程序訪問一個數據塊的過程,如果訪問的資料塊不在buffer cache中,就需要掃描LRU連結串列,當達到掃描塊數限制後還沒有找到空閒buffer,就需要通知DBW0將髒快取回寫到磁碟。分析一下虛擬碼,在這種情況下,使用者程序訪問一個數據塊的過程是最長的,也就是效率最低的。如果一個系統中存在大量的髒緩衝,那麼就可能導致使用者程序訪問資料效能下降。

我們可以通過人工干預將所有髒緩衝回寫到磁碟去,這就是flush buffer。

在9i,可以用以下語句:

alter system set events = 'immediate trace name flush_cache'; --9i

在10g,可以用以下方式(9i的方式在10g仍然有效):

alter system flush buffer_cache; -- 10g

另外,9i的設定事件的方式可以是針對系統全部的,也可以是對會話的(即將該會話造成的髒緩衝回寫)。

1.1.3.2.            Buffer Cache的重要引數配置

Oracle提供了一些引數用於控制Buffer Cache的大小等特性。下面介紹一下這些引數。

·        Buffer Cache的大小配置

由於Buffer Cache中存放的是從資料檔案中來的資料塊的拷貝,因此,它的大小的計算也是以塊的尺寸為基數的。而資料塊的大小是由引數db_block_size指定的。9i以後,塊的大小預設是8K,它的值一般設定為和作業系統的塊尺寸相同或者它的倍數。

而引數db_block_buffers則指定了Buffer Cache中快取塊數。因此,buffer cache的大小就等於db_block_buffers * db_block_size。

在9i以後,Oracle引入了一個新引數:db_cache_size。這個引數可以直接指定Buffer Cache的大小,而不需要通過上面的方式計算出。它的預設值48M,這個數對於一個系統來說一般是不夠用的。

注意:db_cache_size和db_block_buffers是不能同時設定的,否則例項啟動時會報錯。

SQL> alter system set db_block_buffers=16384 scope=spfile;
system altered.
SQL> alter system set db_cache_size=128M scope=spfile;
system altered.
SQL> startup force
ORA-00381: cannot use both new and old parameters for buffer cache size specification

9i以後,推薦使用db_cache_size來指定buffer cache的大小。

在OLTP系統中,對於DB_CACHE_SIZE的設定,我的推薦配置是:

DB_CACHE_SIZE = SGA_MAX_SIZE/2 ~ SGA_MAX_SIZE*2/3

最後,DB_CACHE_SIZE是可以聯機修改的,即例項無需重啟,除非增大Buffer Cache導致SGA實際大小大於SGA_MAX_SIZE。

·        多種塊尺寸系統中的Buffer Cache的配置

從9i開始,Oracle支援建立不同塊尺寸的表空間,並且可以為不同塊尺寸的資料塊指定不同大小的buffer cache。

9i以後,除了SYSTEM表空間和TEMPORARY表空間必須使用標準塊尺寸外,所有其他表空間都可以最多指定四種不同的塊尺寸。而標準塊尺寸還是由上面的所說的引數db_block_size來指定。而db_cache_size則是標緻塊尺寸的buffer cache的大小。

非標準塊尺寸的塊大小可以在建立表空間(CREATE TABLESPACE)是通過BLOCKSIZE引數指定。而不同塊尺寸的buffer cache的大小就由相應引數DB_nK_CACHE_SZIE來指定,其中n可以是2,4,8,16或者32。例如,你建立了一個塊大小為16K的非標準塊尺寸的表空間,你就可以通過設定DB_16K_CACHE_SIZE為來指定快取這個表空間資料塊的buffer cache的大小。

任何一個尺寸的Buffer Cache都是不可以快取其他尺寸的資料塊的。因此,如果你打算使用多種塊尺寸用於你的資料庫的儲存,你必須最少設定DB_CACHE_SIZE和DB_nK_CACHE_SIZE中的一個引數(10g後,指定了SGA_TARGET就可以不需要指定Buffer Cache的大小)。並且,你需要給你要用到的非標準塊尺寸的資料塊指定相應的Buffer Cache大小。這些引數使你可以為系統指定多達4種不同塊尺寸的Buffer Cache。

另外,請注意一點,DB_nK_CACHE_SIZE 引數不能設定標準塊尺寸的緩衝區大小。舉例來說,如果 DB_BLOCK_SIZE 設定為 4K,就不能再設定 DB_4K_CACHE_SIZE 引數。

·        多緩衝池

你可以配置不同的buffer cache,可以達到不同的cache資料的目的。比如,可以設定一部分buffer cache快取過的資料在使用後後馬上釋放,使後來的資料可以立即使用緩衝池;還可以設定資料進入緩衝池後就被keep住不再釋放。部分資料庫物件(表、簇、索引以及分割槽)可以控制他們的資料快取的行為,而這些不同的快取行為就使用不同緩衝池。

o        保持緩衝池(Keep Buffer Pool)用於快取那些永久駐入記憶體的資料塊。它的大小由引數DB_KEEP_CACHE_SZIE控制;

o        回收緩衝池(Recycle Buffer Pool)會立即清除那些不在使用的資料快取塊。它的大小由引數DB_RECYLE_CACHE_SIZE指定;

o        預設的標準快取池,也就是上面所說的DB_CACHE_SIZE指定。

這三個引數相互之間是獨立的。並且他們都只適用於標準塊尺寸的資料塊。與8i相容引數DB_BLOCK_BUFFERS相應的,DB_KEEP_CACHE_SIZE對應有BUFFER_POOL_KEEP、DB_RECYLE_CACHE_SIZE對應有BUFFER_POOL_RECYCLE。同樣,這些引數之間是互斥的,即DB_KEEP_CACHE_SIZE和BUFFER_POOL_KEEP之間只能設定一個。

·        緩衝池建議器

從9i開始,Oracle提供了一些自動優化工具,用於調整系統配置,提高系統性能。建議器就是其中一種。建議器的作用就是在系統執行過程中,通過監視相關統計資料,給相關配置在不同情況下的效能效果,提供給DBA做決策,以選取最佳的配置。

9i中,Buffer Cache就有了相應的建議器。引數db_cache_advice用於該建議器的開關,預設值為FALSE(即關)。當設定它為TRUE後,在系統執行一段時間後,就可以查詢檢視v$db_cache_advice來決定如何使之DB_CACHE_SIZE了。關於這個建議器和檢視,我們會在下面的內容中介紹。

·        其他相關引數

DB_BLOCK_LRU_LATCHES

LRU連結串列作為一個記憶體物件,對它的訪問是需要進行鎖(latch)控制的,以防止多個使用者程序同時使用一個空閒快取塊。DB_BLOCK_LRU_LATCHES設定了LUR latch的數量範圍。Oracle通過一系列的內部檢測來決定是否使用這個引數值。如果這個引數沒有設定,Oracle會自動為它計算出一個值。一般來說,oracle計算出來的值是比較合理,無需再去修改。

9i以後這個引數是隱含引數。對於隱含引數,我建議在沒有得到Oracle支援的情況下不要做修改,否則,如果修改了,Oracle是可以拒絕為你做支援的。

DB_WRITER_PROCESSES

在前面分析Oracle讀取Buffer Cache時,提到一個Oracle重要的後臺程序DBW0,這個(或這些)程序負責將髒快取塊寫回到資料檔案種去,稱為資料庫書寫器程序(Database Writer Process)。DB_WRITER_PROCESSES引數配置寫程序的個數,各個程序以DBWn區分,其中n>=0,是程序序號。一般情況下,DB_WRITER_PROCESSES = MAX(1, TRUNC(CPU數/8))。也就是說,CPU數小於8時,DB_WRITER_PROCESSES為1,即只有一個寫程序DBW0。這對於一般的系統來說也是足夠用。當你的系統的修改資料的任務很重,並且已經影響到效能時,可以調整這個引數。這個引數不要超過CPU數,否則多出的程序也不會起作用,另外,它的最大值不能超過20。

DBWn程序除了上面提到的在使用者程序讀取buffer cache時會被觸發,還能被Checkpoint觸發(Checkpoint是例項從redo log中做恢復的起始點)。

1.1.3.3.            Buffer Cache的重要檢視

關於Buffer Cache,oracle提供一些重要檢視,用於查詢關於Buffer Cache的重要資訊,為調整Buffer Cache、提高效能提供參考。下面一一介紹它們

·        v$db_cache_advice

上面我們提到了Oracle的建議器,其中有一個針對Buffer Cache的建議器。在我們設定了引數db_cache_advice為TRUE後,經過一段時間的系統執行,Oracle收集到相關統計資料,並根據一定的數學模型,預測出DB_CACHE_SIZE在不同大小情況的效能資料。我們就可以由檢視V$DB_CACHE_ADVICE查出這些資料,並根據這些資料調整DB_CACHE_SZIE,使系統性能最優。

下面是關於這個檢視的結構描述:

欄位

資料型別

描述

ID

NUMBER

緩衝池標識號(從1到8,1-6對應於DB_nK_CACHE_SIZE,DB_CACHE_SIZE與系統標準塊尺寸的序號相關,如DB_BLOCK_SIZE為8K,則DB_CACHE_SIZE的標識號為3(2,4,8…)。7是DB_KEEP_CACHE_SIZE,8是DB_RECYCLE_CACHE_SIZE)

NAME

VARCHAR2(20)

緩衝池名稱

BLOCK_SIZE

NUMBER

緩衝池塊尺寸(位元組為單位)

ADVICE_STATUS

VARCHAR2(3)

建議器狀態:ON表示建議器在執行;OFF表示建議器已經關閉。當建議器關閉了,檢視中的資料是上一次開啟所統計得出的。

SIZE_FOR_ESTIMATE

NUMBER

預測效能資料的Cache大小(M為單位)

SIZE_FACTOR

NUMBER

預測的Cache大小因子(即與當前大小的比例)

BUFFERS_FOR_ESTIMATE

NUMBER

預測效能資料的Cache大小(緩衝塊數)

ESTD_PHYSICAL_READ_FACTOR

NUMBER

這一緩衝大小時,物理讀因子,它是如果緩衝大小為SIZE_FOR_ESTIMATE時,建議器預測物理讀數與當前實際物理讀數的比率值。如果當前物理讀數為0,這個值為空。

ESTD_PHYSICAL_READS

NUMBER

如果緩衝大小為SIZE_FOR_ESTIMATE時,建議器預測物理讀數。

下面是從這個檢視中查詢出來的資料:

SQL> select size_for_estimate, estd_physical_read_factor, estd_physical_reads
  2  from v$db_cache_advice
  3  where name = 'DEFAULT';
SIZE_FOR_ESTIMATE ESTD_PHYSICAL_READ_FACTOR ESTD_PHYSICAL_READS
----------------- ------------------------- -------------------
               16                    2.0176             6514226
               32                    1.7403             5619048
               48                    1.5232             4917909
               64                    1.3528             4367839
               80                    1.2698             4099816
               96                    1.1933             3852847
              112                    1.1443             3694709
              128                    1.1007             3553685
              144                    1.0694             3452805
              160                    1.0416             3362964