Oracle記憶體全面分析(ZT)

jolly10發表於2008-07-01

轉自hellodba 的一篇好文.

[@more@]

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

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

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

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

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

1. Oracle的記憶體架構組成

Oracle的記憶體,從總體上講,可以分為兩大塊:共享部分(主要是SGA)和程式獨享部分(主要是PGAUGA)。而這兩部分記憶體裡面,根據功能不同,還分為不同記憶體池(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. SGASystem Global Area

SGASystem Global Area 系統全域性區域)是一組包含一個Oracle例項的資料和控制資訊的共享記憶體結構。這句話可以說是SGA的定義。雖然簡單,但其中闡述了SGA幾個很重要的特性:1SGA的構成——資料和控制資訊,我們下面會詳細介紹;2SGA是共享的,即當有多個使用者同時登入了這個例項,SGA中的資訊可以被它們同時訪問(當涉及到互斥的問題時,由latchenquence控制);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中了,而且不會改變,所以這部分又稱為固定SGAFixed SGA)。這部分割槽域的大小一般小於100K

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

Shared PoolJava PoolLarge PoolStreams Pool這幾塊記憶體區的大小是相應系統引數設定而改變的,所以有通稱為可變SGAVariable 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_SIZE10g中的新記憶體池) + 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工具(本站原創工具,可在中下載)來觀察windowsUnix下的記憶體監控比較複雜,這裡暫不舉例)下引數修改前後的對比。

PRE_PAGE_SGAFALSE

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(實際上,這部分實體記憶體中還有一部分程式的PGAOracle 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 CacheShared 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的的總體情況,只包含兩個欄位:nameSGA記憶體區名字)和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的統計資訊。包含三個欄位:NameSGA記憶體區的名字);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

                    

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

相關文章