Oracle-內存管理解讀

概述

這裏寫圖片描述

關於內存的配置,是最影響 Oracle性能的配置。內存還直接影響到其他兩個重要資源的消耗: CPU 和 IO.

那Oracle 內存存儲的主要內容是什麼呢?

  • 程序代碼( PLSQL、 Java);
  • 關於已經連接的會話的信息,包括當前所有活動和非活動會話;
  • 程序運行時必須的相關信息,例如查詢計劃;
  • Oracle 進程之間共享的信息和相互交流的信息,例如鎖;
  • 那些被永久存儲在外圍存儲介質上,被 cache 在內存中的數據( 如 redo log 條目,數據塊)。

每個 Oracle 數據庫都是由 Oracle Instance(實例)與數據庫(數據文件,控制文件、重做日誌文件)組成,其中所謂實例就是用戶同數據庫交互的媒介,用戶通過於一個實例相連來操作數據庫。

而實例又是由統一的內存結構( SGA,PGA, UGA)和一批內存駐留進程組成。

實例在操作系統中用 ORACLE_SID 來標識,在 Oracle 中用參數 INSTANCE_NAME 來標識, 它們兩個的值是相同的。

數據庫啓動時,系統首先在服務器內存中分配系統全局區( SGA), 構成了 Oracle的內存結構,然後啓動若干個常駐內存的操作系統進程,即組成了 Oracle 的 進程結構,內存區域和後臺進程合稱爲一個 Oracle 實例。


SGA (System Gloable Area)

架構圖

SGA概述

SGA 是一組爲系統分配的共享的內存結構,可以包含一個數據庫實例的數據或控制信息。

如果多個用戶連接到同一個數據庫實例,在實例的 SGA 中,數據可以被多個用戶共享。

當數據庫實例啓動時, SGA 的內存被自動分配;當數據庫實例關閉時, SGA 內存被回收。

SGA 是佔用內存最大的一個區域,同時也是影響數據庫性能的重要因素。

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 中了,而且不會改變,所以這部分又稱爲固定 SGA( Fixed SGA)。這部分區域的大小一般小於 100K。

Shared Pool、 Java Pool、 Large Pool 和 Streams Pool 這幾塊內存區的大小是相應系統參數設置而改變的,所以有通稱爲可變 SGA( Variable SGA)。

這裏寫圖片描述


SGA信息及含義

使用有DBA權限的用戶

SQL> show parameter sga

NAME                                 TYPE        VALUE
------------------- ----------- --------------------------
lock_sga                             boolean     FALSE
pre_page_sga                         boolean     FALSE
sga_max_size                         big integer 6256M
sga_target                           big integer 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

或者查詢v$parameter

select a.name ,a.VALUE ,a.ISMODIFIED ,a.DESCRIPTION  from v$parameter a where a.NAME like '%sga%';
  • 1

這裏寫圖片描述

如果 ISSYS_MODIFIABLE 返回的是 false,說明該參數無法用 alter system語句動態修改,需要重啓數據庫。 
所以 sga_max_size 是不可以動態調整的。但是我們可以對sga_target 進行動態的調整。


SGA_MAX_SIZE

如果發現 SGA 各個內存總和大於 SGA_MAX_SIZE,它會將SGA_MAX_SIZE 的值修改爲 SGA 各個內存區總和的值。

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

對於 OLTP 系統, 一般的建議是將 SGA_MAX_SIZE 設爲物理內存的 60%,PGA 設爲 20%.

下面給出一些參考值:

這裏寫圖片描述


PRE_PAGE_SGA

這個參數的默認值爲FALSE,即不將全部SGA置入物理內存中。當設置爲TRUE時,實例啓動會將全部SGA置入物理內存中。

它可以使實例啓動達到它的最大性能狀態,但是,啓動時間也會更長(因爲爲了使所有SGA都置入物理內存中,oracle進程需要touch所有的SGA頁)。

SQL> alter system set pre_page_sga=true scope=spfile;
  • 1

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中將內存奪回來。

10g 以後有了新特性:自動共享內存管理(Automatic Shared Memory Management ASMM)。

而控制這一特性的,也就僅僅是這一個參數SGA_TARGE

設置這個參數後,就不需要爲每個內存區來指定大小了。SGA_TARGET指定了SGA可以使用的最大內存大小,而SGA中各個內存的大小由Oracle自行控制,不需要人爲指定。Oracle可以隨時調節各個區域的大小,使之達到系統性能最佳狀態的個最合理大小,並且控制他們之和在SGA_TARGET指定的值之內。

一旦給SGA_TARGET指定值後(默認爲0,即沒有啓動ASMM),就自動啓動了ASMM特性。如果不設置SGA_TARGET,則自動共享內存管理功能被禁止。

設置了SGA_TARGET後,以下的SGA內存區就可以由ASMM來自動調整:

  • 共享池(Shared Pool)
  • Java池(Java Pool)
  • 大池(Large Pool)
  • 數據緩存區(Buffer Cache)
  • 流池(Streams Pool)

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

要注意的是:當指定SGA_TARGET小於SGA_MAX_SIZE,實例重啓後,SGA_MAX_SIZE就自動變爲和SGA_TARGET一樣的值了。

在10g中,修改SGA_MAX_SIZE的值還是需要重啓的.

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


SGA組成

Database Buffer Cache

Buffer Cache是SGA區中專門用於存放從數據文件中讀取的的數據塊拷貝的區域。Oracle進程如果發現需要訪問的數據塊已經在buffer cache中,就直接讀寫內存中的相應區域,而無需讀取數據文件,從而大大提高性能.

Buffer cache對於所有oracle進程都是共享的,即能被所有oracle進程訪問。

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

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))/(sum(decode(name, 'db block gets',value,0))
  2           +sum(decode(name,'consistent gets',value,0))))) * 100 "Hit Ratio"  from v$sysstat;

 Hit Ratio
----------
99.6854209
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

一個良好性能的系統,命中率一般保持在95%左右。


Share Pool

SGA中的共享池由庫緩存(Library Cache)、字典緩存(Dictionary Cache)、用於並行執行消息的緩衝以及控制結構組成。

Shared Pool的大小由參數SHARED_POOL_SIZE決定。10g 以後可以通過SGA_TARGET 參數來自動調整。

對於Shared Pool的內存管理,是通過修正過的LRU算法表來實現的。

庫緩存(Library Cache)

Library Cache中包括共享SQL區(Shared SQL Areas)、PL/SQL存儲過程以及控制結構(如鎖、庫緩存句柄)。

任何用戶都可以訪問共享SQL區(可以通過v$sqlarea訪問)。因此庫緩存存在於SGA的共享池中。

共享SQL區和私有SQL區

Oracle會爲每一條SQL語句運行(每運行一條語句Oracle都會打開一個遊標)提供一個共享SQL區(Shared SQL Areas)和私有SQL區(Private SQL Areas屬於PGA)。當發現兩個(或多個)用戶都在運行同一SQL語句時,Oracle會重新組織SQL區,使這些用戶能重用共享SQL區。但他們還會在私有SQL區中保存一份這條SQL語句的拷貝。

一個共享SQL區中保存了一條語句的解析樹和查詢計劃

從解析語句到分配共享SQL區是一個比較消耗CPU的工程。這就是爲什麼我們提倡使用綁定變量的原因了。在沒有使用綁定變量時,語句中的變量的數值不同,oracle就視爲一條新的語句(9i後可以通過cursor_sharing來控制),重複上面的解析、內存分配的動作,將大大消耗系統資源,降低系統性能。

PL/SQL程序單元

Oracle對於PL/SQL程序單元(存儲過程、函數、包、匿名PL/SQL塊和觸發器)的處理過程與SQL的處理方式類似。它會分配一個共享區來存儲被解析、編譯過的程序單元。


字典緩存(Dictionary Cache)

數據字典是有關於數據庫的參考信息、數據庫的結構信息和數據庫中的用戶信息的一組表和視圖的集合,如我們常用到的V$視圖、DBA_視圖都屬於數據字典。

共享池的內存管理

當一條SQL語句被提交給Oracle執行,Oracle會自動執行以下的內存分配步驟:

1.Oracle檢查共享池,看是否已經存在關於這條語句的共享SQL區。如果存在,這個共享SQL區就被用於執行這條語句。而如果不存在,Oracle就從共享池中分配一塊新的共享SQL區給這條語句。同時,無論共享SQL區存在與否,Oracle都會爲用戶分配一塊私有SQL區以保存這條語句相關信息(如變量值)。

2. Oracle爲會話分配一個私有SQL區。私有SQL區的所在與會話的連接方式相關。

在以下情況下,Oracle也會將共享SQL區從共享池中釋放出來:

  • 當使用ANALYZE語句更新或刪除表、簇或索引的統計信息時,所有與被分析對象相關的共享SQL區都被從共享池中釋放掉。當下一次被釋放掉的語句被執行時,又重新在一個新的共享SQL區中根據被更新過的統計信息重新解析。

  • 當對象結構被修改過後,與該對象相關的所有共SQL區都被標識爲無效(invalid)。在下一次運行語句時再重新解析語句。

  • 如果數據庫的全局數據庫名(Global Database Name)被修改了,共享池中的所有信息都會被清空掉。

  • DBA通過手工方式清空共享池:ALTER SYSTEM FLUSH SHARED_POOL;


保留共享池

通過視圖V$SHARED_POOL_RESERVED可以查到保留池的統計信息。其中字段REQUEST_MISSES記錄了沒有立即從空閒列表中得到可用的大內存段請求次數。這個值要爲0。

因爲保留區必須要有足夠個空閒內存來適應那些短期的內存請求,而無需將那些需要長期cache住的沒被pin住的可重建的段清除。否則就需要考慮增大SHARED_POOL_RESERVED_SIZE了。


Shared Pool的重要參數

$sgastat
  • 1

SHARED_POOL_SIZE 
SHARED_POOL_RESERVED_SIZE:指定了共享池中緩存大內存對象的保留區的大小 
_SHARED_POOL_RESERVED_MIN_ALLOC:設置了進入保留區的對象大小的閥值。


Redo Log Buffer重做日誌緩存

Redo Log Buffer是SGA中一段保存數據庫修改信息的緩存。

.重做條目中包含了由於INSERT、UPDATE、DELETE、CREATE、ALTER或DROP所做的修改操作而需要對數據庫重新組織或重做的必須信息。在必要時,重做條目還可以用於數據庫恢復。

參數LOG_BUFFER決定了Redo Log Buffer的大小。它的默認值是512K(一般這個大小都是足夠的),最大可以到4G。10g中可通過參數自動設置。當系統中存在很多的大事務或者事務數量非常多時,可能會導致日誌文件IO增加,降低性能。這時就可以考慮增加LOG_BUFFER。

但是,Redo Log Buffer的實際大小並不是LOB_BUFFER的設定大小。爲了保護Redo Log Buffer,oracle爲它增加了保護頁(一般爲11K)

SQL> show parameter log_buffer

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------
log_buffer                           integer     18317312


SQL>  select * from v$sgastat where name = 'log_buffer';

POOL   NAME                     BYTES
------------ -------------------------- ----------
             log_buffer          18993152
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

大池(large pool)

大池是屬於SGA的可變區(Variable Area)的,它不屬於共享池。

大池中只有兩種內存段:空閒(free)和可空閒(freeable)內存段

large pool是沒有LRU鏈表的。

Java池(Java Pool)

Java池也是SGA中的一塊可選內存區,它也屬於SGA中的可變區。

Java池的內存是用於存儲所有會話中特定Java代碼和JVM中數據。Java池的使用方式依賴與Oracle服務的運行模式。

Java池的大小由參數JAVA_POOL_SIZE設置。Java Pool最大可到1G。

在Oracle 10g以後,提供了一個新的建議器——Java池建議器——來輔助DBA調整Java池大小。建議器的統計數據可以通過視圖V$JAVA_POOL_ADVICE來查詢


流池(Streams Pool)

流池是Oracle 10g中新增加的。是爲了增加對流的支持。

流池也是可選內存區,屬於SGA中的可變區。它的大小可以通過參數STREAMS_POOL_SIZE來指定。

如果沒有被指定,oracle會在第一次使用流時自動創建。如果設置了SGA_TARGET參數,Oracle會從SGA中分配內存給流池;

如果沒有指定SGA_TARGET,則從buffer cache中轉換一部分內存過來給流池。轉換的大小是共享池大小的10%。

Oracle同樣爲流池提供了一個建議器——流池建議器。建議器的統計數據可以通過視圖V$STREAMS_POOL_ADVICE查詢。


PGA(Program Global Area)

PGA由兩組區域組成:固定PGA和可變PGA

它的內存段可以通過視圖X$KSMPP(另外一個視圖X$KSMSP可以查到可變SGA的內存段信息,他們的結構相同)查到。

PGA堆包含用於存放X$表的的內存(依賴與參數設置,包括DB_FILES、CONTROL_FILES)。

總的來說,PGA的可變區中主要分爲以下三部分內容:

  • 1)私有SQL區;
  • 2)遊標和SQL區
  • 3)會話內存

UGA ( The User Global Area)

UGA(User Global Area用戶全局區)由用戶會話數據、遊標狀態和索引區組成。

PGA是服務於進程的,它包含的是進程的信息;而UGA是服務於會話的,它包含的是會話的信息


CGA ( The Call Global Area)

與其他的全局區不同,CGA(Call Global Area調用全局區)的存在是瞬間的。它只存在於一個調用過程中。對於實例的一些低層次的調用需要CGA,包括: 
1)解析一條SQL語句; 
2)執行一條SQL語句; 
3)取一條SELECT語句的輸出值。

Java調用內存也分配在CGA中。它被分爲三部分空間:堆空間、新空間和老空間。


軟件代碼區(Software Code Area)

軟件代碼區是一部分用於存放那些正在運行和可以被運行的代碼(Oracle自身的代碼)的內存區。Oracle代碼一般存儲在一個不同於用戶程序存儲區的軟件代碼區,而用戶程序存儲區是排他的、受保護的區域。

發佈了284 篇原創文章 · 獲贊 69 · 訪問量 272萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章