Oracle記憶體詳解之一 整體架構
Oracle的記憶體配置與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
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)
其他資訊(如資料庫和例項的狀態資訊)
SQL>show sga
TotalSystem Global Area 612368384 bytes
FixedSize 1250428 bytes
VariableSize 192940932 bytes
DatabaseBuffers 411041792 bytes
RedoBuffers 7135232 bytes
SGA 中的資料字典快取 和其他資訊會被例項的後臺程式所訪問,它們在例項啟動後就固定在SGA中了,而且不會改變,所以這部分又稱為固定SGA(Fixed SGA)。這部分割槽域的大小一般小於100K。
Shared Pool、Java Pool、Large Pool和Streams Pool這幾塊記憶體區的大小是相應系統引數設定而改變的,所以有通稱為可變SGA(Variable SGA)。
透過下面的語句查詢
SQL>show parameter sga
NAME TYPE VALUE
----------------------------------------------- -------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 584M
sga_target big integer 584M
先對這幾個引數做一下說明:
SQL> select name,value ,ISSYS_MODIFIABLEfrom v$parameter where name like 'sga%';
NAME VALUE ISSYS_MOD
------------------------------ ---------
sga_max_size 612368384 FALSE
sga_target 612368384 IMMEDIATE
如果ISSYS_MODIFIABLE 返回的是false,說明該引數無法用alter system語句動態修改,需要重啟資料庫。
所以sga_max_size 是不可以動態調整的。但是我們可以對sga_target 進行動態的調整。
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 設為實體記憶體的60%,PGA 設為20%。 但是現在伺服器記憶體是相當大的。 幾百G的記憶體隨處可見。60%也就是幾百G記憶體。顯然這樣也是不合適的。 所以要根據自己系統來設定設定這個值。這個也就所說的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到相應的頁時,才會置換到實體記憶體中。我們可以透過設定PRE_PAGE_SGA引數,讓例項一啟動後,所有SGA都分配到實體記憶體。
這個引數的預設值為FALSE,即不將全部SGA置入實體記憶體中。當設定為TRUE時,例項啟動會將全部SGA置入實體記憶體中。它可以使例項啟動達到它的最大效能狀態,但是,啟動時間也會更長(因為為了使所有SGA都置入實體記憶體中,Oracle程式需要touch所有的SGA頁)。
SQL>alter system set pre_page_sga=true scope=spfile;
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 ManagementASMM)。而控制這一特性的,也就僅僅是這一個引數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一樣的值了。
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錯誤的機率。
1.1Database Buffer Cache
BufferCache是SGA區中專門用於存放從資料檔案中讀取的的資料塊複製的區域。Oracle程式如果發現需要訪問的資料塊已經在buffer cache中,就直接讀寫記憶體中的相應區域,而無需讀取資料檔案,從而大大提高效能(記憶體的讀取效率是磁碟讀取效率的14000倍)。Buffer cache對於所有Oracle程式都是共享的,即能被所有Oracle程式訪問。
和Shared Pool一樣,buffer cache被分為多個集合,這樣能夠大大降低多CPU系統中的爭用問題。
1.1.1Buffer cache的管理
Oracle對於buffer cache的管理,是透過兩個重要的連結串列實現的:寫連結串列和最近最少使用連結串列(the Least Recently Used LRU)。寫連結串列所指向的是所有髒資料塊快取(即被程式修改過,但還沒有被回寫到資料檔案中去的資料塊,此時緩衝中的資料和資料檔案中的資料不一致)。而LRU連結串列指向的是所有空閒的快取、pin住的快取以及還沒有來的及移入寫連結串列的髒快取。空閒快取中沒有任何有用的資料,隨時可以使用。而pin住的快取是當前正在被訪問的快取。LRU連結串列的兩端就分別叫做最近使用端(the Most Recently Used MRU)和最近最少使用端(LRU)。
1)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 blockgets',value,0))
+sum(decode(name,'consistentgets',value,0))))) * 100 "Hit Ratio" from v$sysstat;
Hit Ratio
----------
98.3471481
一個良好效能的系統,命中率一般保持在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程式將髒快取寫入磁碟去。
2) 全表掃描
當發生全表掃描(Full Table Scan)時,使用者程式讀取表的資料塊,並將他們放在LRU連結串列的LRU端(和上面不同,不是放在MRU端)。這樣做的目的是為了使全表掃描的資料儘快被移出。因為全表掃描一般發生的頻率較低,並且全表掃描的資料塊大部分在以後都不會被經常使用到。
而如果你希望全表掃描的資料能被cache住,使之在掃描時放在MRU端,可以透過在建立或修改表(或簇)時,指定CACHE引數。
3) FlushBuffer
回顧一下前面一個使用者程式訪問一個資料塊的過程,如果訪問的資料塊不在buffer cache中,就需要掃描LRU連結串列,當達到掃描塊數限制後還沒有找到空閒buffer,就需要通知DBW0將髒快取回寫到磁碟。如果一個系統中存在大量的髒緩衝,那麼就可能導致使用者程式訪問資料效能下降。
我們可以透過人工干預將所有髒緩衝回寫到磁碟去,這就是flush buffer。
在9i,可以用以下語句:
altersystem set events = 'immediate trace name flush_cache'; --9i
在10g,可以用以下方式(9i的方式在10g仍然有效):
altersystem flush buffer_cache; -- 10g
另外,9i的設定事件的方式可以是針對系統全部的,也可以是對會話的(即將該會話造成的髒緩衝回寫)。
1.1.2Buffer Cache的重要引數配置
1) BufferCache的大小配置
由於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,這個數對於一個系統來說一般是不夠用的。
10G中提供了自動記憶體管理,透過使用sga_target在在多個元件間自動分配記憶體以保證最有效的記憶體使用.如shared pool javapool largepool buffer cache都無需顯式設定這些元件的大小,預設都是0,當某個元件需要記憶體時,可以透過內部自動調整機制請求記憶體轉移.
注意:db_cache_size和db_block_buffers是不能同時設定的,否則例項啟動時會報錯。
SQL>alter system set db_block_buffers=16384 scope=spfile;
systemaltered.
SQL>alter system set db_cache_size=20M scope=memory;
systemaltered.
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。
2) 多種塊尺寸系統中的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引數。
3)多緩衝池
可以配置不同的buffer cache,可以達到不同的cache資料的目的。比如,可以設定一部分buffer cache快取過的資料在使用後後馬上釋放,使後來的資料可以立即使用緩衝池;還可以設定資料進入緩衝池後就被keep住不再釋放。部分資料庫物件(表、簇、索引以及分割槽)可以控制他們的資料快取的行為,而這些不同的快取行為就使用不同緩衝池。
保持緩衝池(Keep Buffer Pool)用於快取那些永久駐入記憶體的資料塊。它的大小由引數DB_KEEP_CACHE_SZIE控制;
回收緩衝池(Recycle Buffer Pool)會立即清除那些不在使用的資料快取塊。它的大小由引數DB_RECYLE_CACHE_SIZE指定;
預設的標準快取池,也就是上面所說的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之間只能設定一個。
4) 緩衝池建議器
從9i開始,Oracle提供了一些自動最佳化工具,用於調整系統配置,提高系統效能。建議器就是其中一種。建議器的作用就是在系統執行過程中,透過監視相關統計資料,給相關配置在不同情況下的效能效果,提供給DBA做決策,以選取最佳的配置。
9i中,Buffer Cache就有了相應的建議器。引數db_cache_advice用於該建議器的開關,預設值為FALSE(即關)。當設定它為TRUE後,在系統執行一段時間後,就可以查詢檢視v$db_cache_advice來決定如何使之DB_CACHE_SIZE了。關於這個建議器和檢視,我們會在下面的內容中介紹。
5) 其他相關引數
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.2 SharePool
SGA中的共享池由庫快取(Library Cache)、字典快取(Dictionary Cache)、用於並行執行訊息的緩衝以及控制結構組成。
SharedPool的大小由引數SHARED_POOL_SIZE決定。9i中,在32位系統下,這個引數的預設值是8M,而64位系統下的預設值位64M。最大為4G。 10g 以後可以透過SGA_TARGET 引數來自動調整。
對於Shared Pool的記憶體管理,是透過修正過的LRU演算法表來實現的。
1.2.1 庫快取(Library Cache)
LibraryCache中包括共享SQL區(Shared SQL Areas)、PL/SQL儲存過程以及控制結構(如鎖、庫快取控制程式碼)。
任何使用者都可以訪問共享SQL區(可以透過v$sqlarea訪問,隨後會介紹這個重要檢視)。因此庫快取存在於SGA的共享池中。
1) 共享SQL區和私有SQL區
Oracle會為每一條SQL語句執行(每執行一條語句Oracle都會開啟一個遊標)提供一個共享SQL區(Shared SQL Areas)和私有SQL區(Private SQL Areas屬於PGA)。當發現兩個(或多個)使用者都在執行同一SQL語句時,Oracle會重新組織SQL區,使這些使用者能重用共享SQL區。但他們還會在私有SQL區中儲存一份這條SQL語句的複製。
一個共享SQL區中儲存了一條語句的解析樹和查詢計劃。在多使用者系統中,Oracle透過為SQL語句使用同一共享SQL區多次執行來節省記憶體。
當一條新的SQL語句被解析時,Oracle從共享池中分配一塊記憶體來儲存共享SQL區。這塊記憶體的大小與這條語句的複雜性相關。如果Shared Pool不夠空間分配給共享SQL區,Oracle將釋放從LRU連結串列中查詢到最近最少使用的記憶體塊,直到有足夠空間給新的語句的共享SQL區。如果Oracle釋放的是一個共享SQL區的記憶體,那麼相應的語句在下次執行時需要再次解析並重新分配共享SQL區。而從解析語句到分配共享SQL區是一個比較消耗CPU的工程。這就是為什麼我們提倡使用繫結變數的原因了。在沒有使用繫結變數時,語句中的變數的數值不同,Oracle就視為一條新的語句(9i後可以透過cursor_sharing來控制),重複上面的解析、記憶體分配的動作,將大大消耗系統資源,降低系統效能。
2) PL/SQL程式單元
Oracle對於PL/SQL程式單元(儲存過程、函式、包、匿名PL/SQL塊和觸發器)的處理過程和對單個的SQL語句的處理過程相似。它會分配一個共享區來儲存被解析、編譯過的程式單元。同時分配一個私有區域來存放執行程式單元的會話所指定的程式單元的引數值(包括本地變數、全域性變數和包變數——這也叫做包的例項化)和用於執行程式所需的記憶體。如果多個使用者執行同一個程式單元,則他們共享同一個共享區域,並且各自保持一份私有區域,用於使用者會話中指定的變數值。
而一個PL/SQL程式單元中的每條單個SQL語句的處理過程則和上面描述的SQL語句的處理過程相同。要注意一點,儘管這些語句是從PL/SQL程式單元中來的,但是Oracle還是會為這些語句分配一塊共享SQL區,同時為每個使用者分配一個相應的私有SQL區。
1.2.2 字典快取(Dictionary Cache)
資料字典是有關於資料庫的參考資訊、資料庫的結構資訊和資料庫中的使用者資訊的一組表和檢視的集合,如我們常用到的V$檢視、DBA_檢視都屬於資料字典。在SQL語句解析的過程中,Oracle可以非常迅速的訪問(如果需要的話)這些資料字典,在SQL Trace中,這種對資料字典的訪問就被統計為回撥(recursive calls)。
因為Oracle對資料字典訪問如此頻繁,因此記憶體中有兩處地方被專門用於存放資料字典。一個地方就是資料字典快取(Data Dictionary Cache)。資料字典快取也被稱為行快取(Row Cache),因為它是以記錄行為單元儲存資料的,而不像Buffer Cache是以資料塊為單元儲存資料。記憶體中另外一個儲存資料字典的地方是庫快取。所有Oracle的使用者都可以訪問這兩個地方以獲取資料字典資訊。
1.2.3 共享池的記憶體管理
通常來說,共享池是根據修正過的LRU演算法來是否其中的物件(共享SQL區和資料自動記錄行)的,否則這些物件就一直保持在共享池中。如果共享池需要為一個新物件分配記憶體,並且共享池中沒有足夠記憶體時,記憶體中那些不經常使用的物件就被釋放掉。一個被許多會話使用過的共享池物件,即使最初建立它的程式已經結束,只要它是有用的,都會被修正過的LRU演算法一直保持在共享池中。這樣就使一個多使用者的Oracle系統對SQL語句的處理和記憶體消耗最小。
注意,即使一個共享SQL區與一個開啟的遊標相關,但如果它長時間沒有被使用,它還是可能會被從共享池中釋放出來。而此時如果開啟的遊標還需要執行它的相關語句,Oracle就會重新解析語句,並分配新的共享SQL區。
當一條SQL語句被提交給Oracle執行,Oracle會自動執行以下的記憶體分配步驟:
1. Oracle檢查共享池,看是否已經存在關於這條語句的共享SQL區。如果存在,這個共享SQL區就被用於執行這條語句。而如果不存在,Oracle就從共享池中分配一塊新的共享SQL區給這條語句。同時,無論共享SQL區存在與否,Oracle都會為使用者分配一塊私有SQL區以儲存這條語句相關資訊(如變數值)。
2. Oracle為會話分配一個私有SQL區。私有SQL區的所在與會話的連線方式相關。
在以下情況下,Oracle也會將共享SQL區從共享池中釋放出來:
1)當使用ANALYZE語句更新或刪除表、簇或索引的統計資訊時,所有與被分析物件相關的共享SQL區都被從共享池中釋放掉。當下一次被釋放掉的語句被執行時,又重新在一個新的共享SQL區中根據被更新過的統計資訊重新解析。
2) 當物件結構被修改過後,與該物件相關的所有共SQL區都被標識為無效(invalid)。在下一次執行語句時再重新解析語句。
3)如果資料庫的全域性資料庫名(Global Database Name)被修改了,共享池中的所有資訊都會被清空掉。
4)DBA透過手工方式清空共享池:ALTER SYSTEM FLUSH SHARED_POOL;
SharedPool能被分成幾個區域,分別被不同的latch(latch數最大為7,可以透過隱含引數_kghdsidx_count設定)保護。
表x$kghlu可以檢視shared pool中的LRU列表。當滿足以下條件之一時,shared pool會分為多個區,分別有不同的LRU連結串列管理:
1)在10g之前版本,如果shared pool大於128M、CPU數量大於4;
2)Oracle資料庫版本為10g
這時,在x$kghlu中就會對應不同記錄。
1.2.4 保留共享池
前面提到,如果Oracle解析一個 PL/SQL程式單元,也需要從共享池中分配記憶體給這些程式單元物件。由於這些物件本一般比較大(如包),所以分配的記憶體空間也相對較大。系統經過長時間執行後,共享池可能存在大量記憶體碎片,導致無法滿足對於大塊記憶體段的分配。
為了使有足夠空間快取大程式塊,Oracle專門從共享池內建出一塊區域來來分配記憶體保持這些大塊。這個保留共享池的預設大小是共享池的5%。它的大小也可以透過引數SHARED_POOL_RESERVED_SIZE來調整。保留區是從共享池中分配,不是直接從SGA中分配的,它是共享池的保留部分,用於儲存大塊段。
SharedPool中記憶體大於5000位元組的大段就會被存放在共享池的保留部分。而這個大小限制是透過隱含引數_SHARED_POOL_RESERVED_MIN_ALLOC來設定的(如前面所說,隱含引數不要去修改它)。除了在例項啟動過程中,所有小於這個數的記憶體段永遠都不會放到保留部分中,而大於這個值的大記憶體段也永遠不會存放到非保留區中,即使共享池的空間不夠用的情況下也是如此。
保留區的空閒記憶體也不會被包含在普通共享池的空閒列表中。它會維護一個單獨的空閒列表。保留池也不會在它的LRU列表中存放可重建(Recreatable關於記憶體段的各種狀態我們在後面的內容中再介紹)段。當釋放普通共享池空閒列表上的記憶體時是不會清除這些大段的,同樣,在釋放保留池的空閒列表上的大記憶體段時也不會清除普通共享池中記憶體。
透過檢視V$SHARED_POOL_RESERVED可以查到保留池的統計資訊。其中欄位REQUEST_MISSES記錄了沒有立即從空閒列表中得到可用的大記憶體段請求次數。這個值要為0。因為保留區必須要有足夠個空閒記憶體來適應那些短期的記憶體請求,而無需將那些需要長期cache住的沒被pin住的可重建的段清除。否則就需要考慮增大SHARED_POOL_RESERVED_SIZE了。
可以透過觀察檢視V$SHARED_POOL_RESERVED的MAX_USED_SPACE欄位來判斷保留池的大小是否合適。大多數情況下,你會觀察到保留池是很少被使用的,也就是說5%的保留池空間可能有些浪費。但這需要經過長期觀察來決定是否需要調整保留池大小。
保留區使用shared pool的LRU連結串列來管理記憶體塊,但是在做掃描時,相互是不受影響的。例如,記憶體管理器掃描shared pool的LRU連結串列,清出空間以分配給一個小於5000位元組的記憶體請求,是不會清出保留區的記憶體塊的,相反亦然。
1.2.5 將重要、常用物件保持(Keep)在共享池中
根據LRU演算法,一些一段時間沒有使用到的記憶體塊會被情況釋放。這就可能導致一些重要的物件(如一個含有大量通用演算法函式的包、被cache的序列)被從記憶體中清除掉。這些物件可能只是間歇被使用,但是因為他們的處理過程複雜(不僅包本身重新分配記憶體、解析,還要檢查裡面的所有語句),要在記憶體中重建他們的代價非常大。
我們可以透過呼叫儲存過程DBMS_SHARED_POOL.KEEP將這些物件保持在共享池中來降低這種風險。這個儲存過程立即將物件及其從事物件載入library cache中,並將他們都標記為保持(Keeping)狀態。對於這種物件,我們建議在例項啟動時就Keep住,以減少記憶體碎片的機率。
有一種觀點認為那些大物件(如包)是沒有必要被Keep住的,因為他們會被保持在共享池的保留區(如前所述,這個區通常使用率很低),所以一般不可能被清出。這個觀點是錯誤滴!因為大多數大物件實際上是被分為多個小的記憶體段被載入共享池的,因此根本不會因為物件的大小而受到特別的保護。
另外,也不要透過頻繁呼叫某些物件以防止他們被從共享池中清出。如果共享池大小設定合理,在系統執行的高峰時期,LRU連結串列會相對較短,那些沒有被pin住的物件會很快被清出,除非他們被keep住了。
1.2.6 關於Shared Pool的重要引數
1) SHARED_POOL_SIZE
它指定了Shared Pool的大小。9i下,在32位系統中,這個引數的預設值是8M,而64位系統中的預設值位64M。
但是,在SGA中還存在一塊叫內部SGA消耗(Internal SGA Overhead)的記憶體被放置在共享池中。在9i及之前版本,共享池的統計大小(透過v$sgastat檢視統計)為SHARED_POOL_SIZE +內部SGA消耗大小。而10g以後,SHARED_POOL_SIZE就已經包含了這部分記憶體大小。因此在10g中,共享池的實際使用大小就是SHARED_POOL_SIZE -內部SGA消耗大小,這在配置共享池大小時需要考慮進去,否則,扶過SHARED_POOL_SIZE設定過小,在例項啟動時就會報ORA-00371錯誤。
2) SHARED_POOL_RESERVED_SIZE
這個引數前面已經提到,指定了共享池中快取大記憶體物件的保留區的大小。這裡不再贅述。
3) _SHARED_POOL_RESERVED_MIN_ALLOC
這個引數前面也已經介紹,設定了進入保留區的物件大小的閥值。
1.3 重做日誌快取(Redo Log Buffer)
Redo LogBuffer是SGA中一段儲存資料庫修改資訊的快取。這些資訊被儲存在重做條目(Redo Entry)中.重做條目中包含了由於INSERT、UPDATE、DELETE、CREATE、ALTER或DROP所做的修改操作而需要對資料庫重新組織或重做的必須資訊。在必要時,重做條目還可以用於資料庫恢復。
重做條目是Oracle資料庫程式從使用者記憶體中複製到Redo Log Buffer中去的。重做條目在記憶體中是連續相連的。後臺程式LGWR負責將Redo Log Buffer中的資訊寫入到磁碟上活動的重做日誌檔案(Redo Log File)或檔案組中去的。
引數LOG_BUFFER決定了Redo Log Buffer的大小。它的預設值是512K(一般這個大小都是足夠的),最大可以到4G。10g中可透過引數自動設定。當系統中存在很多的大事務或者事務數量非常多時,可能會導致日誌檔案IO增加,降低效能。這時就可以考慮增加LOG_BUFFER。
但是,Redo Log Buffer的實際大小並不是LOB_BUFFER的設定大小。為了保護Redo Log Buffer,Oracle為它增加了保護頁(一般為11K):
SQL>select * from v$sgastat where name = 'log_buffer';
POOL NAME BYTES
-------------------------------------- ----------
log_buffer 7135232
SQL>show parameter log_buffer
NAME TYPE VALUE
----------------------------------------------- ----------------
log_buffer integer 7024640
SQL>
1.4 大池(large pool)
大池是SGA中的一塊可選記憶體池,根據需要時配置。在以下情況下需要配置大池:
1) 用於共享服務(Shared Server MTS方式中)的會話記憶體和Oracle分散式事務處理的Oracle XA介面
2) 使用並行查詢(Parallel Query Option PQO)時
3) IO服務程式
4) Oracle備份和恢復操作(啟用了RMAN時)
透過從大池中分配會話記憶體給共享服務、Oracle XA或並行查詢,Oracle可以使用共享池主要來快取共享SQL,以防止由於共享SQL快取收縮導致的效能消耗。此外,為Oracle備份和恢復操作、IO服務程式和並行查詢分配的記憶體一般都是幾百K,這麼大的記憶體段從大池比從共享池更容易分配得到。
引數LARGE_POOL_SIZE設定大池的大小。大池是屬於SGA的可變區(Variable Area)的,它不屬於共享池。對於大池的訪問,是受到large memory latch保護的。大池中只有兩種記憶體段:空閒(free)和可空閒(freeable)記憶體段。它沒有可重建(recreatable)記憶體段,因此也不用LRU連結串列來管理(這和其他記憶體區的管理不同)。大池最大大小為4G。
為了防止大池中產生碎片,隱含引數_LARGE_POOL_MIN_ALLOC設定了大池中記憶體段的最小大小,預設值是16K(同樣,不建議修改隱含引數)。
此外,large pool是沒有LRU連結串列的。
1. 5 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來查詢。如何藉助建議器調整Java池的方法和使用Buffer Cache建議器類似,可以參考Buffer Cache中關於建議器部分。
1.6 流池(Streams Pool)
流池是Oracle 10g中新增加的。是為了增加對流的支援。
流池也是可選記憶體區,屬於SGA中的可變區。它的大小可以透過引數STREAMS_POOL_SIZE來指定。如果沒有被指定,Oracle會在第一次使用流時自動建立。如果設定了SGA_TARGET引數,Oracle會從SGA中分配記憶體給流池;如果沒有指定SGA_TARGET,則從buffer cache中轉換一部分記憶體過來給流池。轉換的大小是共享池大小的10%。
Oracle同樣為流池提供了一個建議器——流池建議器。建議器的統計資料可以透過檢視V$STREAMS_POOL_ADVICE查詢。使用方法參看Buffer Cache中關於最佳化器部分。
二. PGA
PGA(Program Global Area程式全域性區)是一塊包含一個服務程式的資料和控制資訊的記憶體區域。它是Oracle在一個服務程式啟動是建立的,是非共享的。一個Oracle程式擁有一個PGA記憶體區。一個PGA也只能被擁有它的那個服務程式所訪問,只有這個程式中的Oracle程式碼才能讀寫它。因此,PGA中的結構是不需要Latch保護的。
我們可以設定所有服務程式的PGA記憶體總數受到例項分配的總體PGA(Aggregated PGA)限制。
在專有伺服器(Dedicated Server)模式下,Oracle會為每個會話啟動一個Oracle程式;而在多執行緒服務(Multi-Thread Server MTS)模式下,由多個會話共享通一個Oracle服務程式。
PGA中包含了關於程式使用到的作業系統資源的資訊,以及一些關於程式狀態的資訊。而關於程式使用的Oracle共享資源的資訊則是在SGA中。這樣做可以使在程式以外中止時,能夠及時釋放和清除這些資源。
?StackSpace是用來儲存使用者會話變數和陣列的儲存區域;
?UserSession Data是為使用者會話使用的附加儲存區。
|--SessionInformation
|--SortArea
|--CursorInformation
注意Session information(使用者會話資訊)在獨佔伺服器中與在共享伺服器中所處的記憶體區域是不同的。
2.1 PGA的組成
PGA由兩組區域組成:固定PGA和可變PGA(或者叫PGA堆,PGA Heap【堆——Heap就是一個受管理的記憶體區】)。固定PGA和固定SGA類似,它的大小時固定的,包含了大量原子變數、小的資料結構和指向可變PGA的指標。
可變PGA是一個記憶體堆。它的記憶體段可以透過檢視X$KSMPP(另外一個檢視X$KSMSP可以查到可變SGA的記憶體段資訊,他們的結構相同)查到。PGA堆包含用於存放X$表的的記憶體(依賴與引數設定,包括DB_FILES、CONTROL_FILES)。
總的來說,PGA的可變區中主要分為以下三部分內容:
1)私有SQL區;
2)遊標和SQL區
3)會話記憶體
2.1.1 私有SQL區(Private SQL Area)
私有SQL區包含了繫結變數值和執行時期記憶體結構資訊等資料。每一個執行SQL語句的會話都有一個塊私有SQL區。所有提交了相同SQL語句的使用者都有各自的私有SQL區,並且他們共享一個共享SQL區。因此,一個共享SQL區可能和多個私有共享區相關聯。
一個遊標的私有SQL區又分為兩個生命週期不同的區:
永久區:包含繫結變數資訊。當遊標關閉時被釋放。
執行區:當執行結束時釋放。
建立執行區是一次執行請求的第一步。對於INSERT、UPDATE和DELETE語句,Oracle在語句執行結束時釋放執行區。對於查詢操作,Oracle只有在所有記錄被fetch到或者查詢被取消時釋放執行區。
2.1.2 遊標和SQL區(Cursors and SQL Areas)
一個Oracle預編譯程式或OCI程式的應用開發人員能夠很明確的開啟一個遊標,或者控制一塊特定的私有SQL區,將他們作為程式執行的命名資源。另外,Oracle隱含的為一些SQL語句產生的遞迴呼叫(前面有介紹,讀取資料字典資訊)也使用共享SQL區。
私有SQL區是由使用者程式管理的。如何分配和釋放私有SQL區極大的依賴與你所使用的應用工具。而使用者程式可以分配的私有SQL區的數量是由引數OPEN_CURSORS控制的,它的預設值是50。
在遊標關閉前或者語句控制程式碼被釋放前,私有SQL區將一直存在(但其中的執行區是在語句執行結束時被釋放,只有永久區一直存在)下去。應用開發人員可以透過將所有開啟的不再使用的遊標都關閉來釋放永久區,以減少使用者程式所佔用的記憶體。
2.1.3 會話記憶體(Session Memory)
會話記憶體是一段用於儲存會話變數(如登入資訊)和其他預會話相關資訊的記憶體。對於共享伺服器模式下,會話記憶體是共享的,而不是私有的。
對於複雜的查詢(如決策支援系統中的查詢),執行區的很大一部分被那些記憶體需求很大的操作分配給SQL工作區(SQL Work Area)。這些操作包括:
基於排序的操作(ORDER BY、GROUP BY、ROLLUP、視窗函式);
Hash Join
Bitmapmerge
Bitmapcreate
例如,一個排序操作使用工作區(這時也可叫排序區Sort Area)來將一部分資料行在記憶體排序;而一個Hash Join操作則使用工作區(這時也可以叫做Hash區 Hash Area)來建立Hash表。如果這兩種操作所處理的資料量比工作區大,那就會將輸入的資料分成一些更小的資料片,使一些資料片能夠在記憶體中處理,而其他的就在臨時表空間的磁碟上稍後處理。儘管工作區太小時,Bitmap操作不會將資料放到磁碟上處理,但是他們的複雜性是和工作區大小成反比的。因此,總的來說,工作區越大,這些操作就執行越快。
工作區的大小是可以調整的。一般來說,大的工作區能讓一些特定的操作效能更佳,但也會消耗更多的記憶體。工作區的大小足夠適應輸入的資料和相關的SQL操作所需的輔助的記憶體就是最優的。如果不滿足,因為需要將一部分資料放到臨時表空間磁碟上處理,操作的響應時間會增長。
2.2 PGA記憶體自動管理
SQL工作區可以是自動的、全域性的管理。DBA只要設定引數PGA_AGGREGATE_TARGET給一個例項的PGA記憶體指定總的大小。設定這個引數後,Oracle將它作為一個總的全侷限制值,儘量使所有Oracle服務程式的PGA記憶體總數不超過這個值。
在這個引數出現之前,DBA要調整引數SORT_AREA_SIZE、 HASH_AREA_SIZE,、BITMAP_MERGE_AREA_SIZE和CREATE_BITMAP_AREA_SIZE(關於這些引數,我們會在後面介紹),使效能和PGA記憶體消耗最佳。對這些引數的調整是非常麻煩的,因為即要考慮所有相關的操作,使工作區適合它們輸入資料大小,又要使PGA記憶體不消耗過大導致系統整體效能下降。
9i以後,透過設定了引數PGA_AGGREGATE_TARGET,使所有會話的工作區的大小都是自動分配。同時,所有*_AREA_SIZE引數都會失效。在任何時候,例項中可用於工作區的PGA記憶體總數都是基於引數PGA_AGGREGATE_TARGET的。工作區記憶體總數等於引數PGA_AGGREGATE_TARGET的值減去系統其他元件(如分配給會話的PGA記憶體)的記憶體消耗。分配給Oracle程式的PGA記憶體大小是根據它們對記憶體的需求情況來的。
引數WORKAREA_SIZE_POLICY決定是否使用PGA_AGGREGATE_TARGET來管理PGA記憶體。它有兩個值:AUTO和MANUAL。預設是AUTO,即使用PGA_AGGREGATE_TARGET來管理PGA記憶體。其實,從引數WORKAREA_SIZE_POLICY的名字上可以看出,Oracle的PGA記憶體自動管理只會調整工作區部分,而非工作區部分(固定PGA區)則不會受影響。
還有注意一點就是:10g之前,PGA_AGGREGATE_TARGET只在專用服務模式下生效。而10g以後,PGA記憶體自動管理在專有服務模式(Dedicated Server)和MTS下都有效。另外,9i在OpenVMS系統上還不支援PGA記憶體自動管理,但10g支援。
設定了PGA_AGGREGATE_TARGET以後,每個程式PGA記憶體的大小也是受限制的:
序列操作時,每個程式可用的PGA記憶體為MIN(PGA_AGGREGATE_TARGET * 5%,_pga_max_size/2),其中隱含引數_pga_max_size的預設值是200M,同樣不建議修改它。
並行操作時,並行語句可用的PGA記憶體為PGA_AGGREGATE_TARGET * 30% / DOP(Degree Of Parallelism並行度)。
2.3 專有服務(Dedicated Server)和共享服務(Shared Server)
對PGA記憶體的管理和分配,很大程度上依賴與服務模式。下面這張表顯示了在不同模式下,PGA記憶體不同部分的分配的異同:
記憶體區
專有服務
共享服務
會話記憶體
私有的
共享的
永久區所在區域
PGA
SGA
SELECT語句的執行區所在區域
PGA
PGA
DML/DDL語句的執行區所在區域
PGA
PGA
三. UGA (The User Global Area)
PGA是一段包含一個Oracle服務或後臺程式的資料和控制資訊的記憶體。PGA的大小依賴與系統的配置。在專用服務(Dedicated Server)模式下,一個服務程式與一個使用者程式相關,PGA就包括了堆空間和UGA。而UGA(User Global Area使用者全域性區)由使用者會話資料、遊標狀態和索引區組成。在共享服務(MTS)模式下,一個共享服務程式被多個使用者程式共享,此時UGA是Shared Pool或Large Pool的一部分(依賴與配置)。
許多DBA都不理解PGA和UGA之間的區別。其實這種區別可以簡單的理解為程式和會話直接的區別。在專用服務模式下,程式和會話是一對一的;而在MTS模式下,程式和會話是一對多的關係。PGA是服務於程式的,它包含的是程式的資訊;而UGA是服務於會話的,它包含的是會話的資訊。因此,MTS模式下,PGA和UGA之間的關係也是一對多的。
UGA中包含了一個會話的資訊,包括:
1)開啟遊標的永久區和執行區;
2)包的狀態資訊,特別是包的變數;
3)Java會話的資訊;
4)啟用的角色;
5)啟用的跟蹤事件(ALTER SESSION SET EVENT …);
6)起作用的NLS引數(SELECT * FROM NLS_SESSION_PARAMETERS;);
7)所有開啟的db link;
8)會話對於信任的Oracle的託管訪問標記(mandatory access control (MAC)
和PGA一樣,UGA也由兩組區組成,固定UGA和可變UGA(或者說UGA堆)。固定UGA包含了大概70個原子變數、小的資料結構以及指向UGA堆的指標。
UGA heap中的段可以透過表X$KSMUP查到(它的結構和X$KSMSP相同)。UGA堆包含了儲存一些固定表(X$表)的永久記憶體(依賴與特定引數的設定,如OPEN_CURSORS,OPEN_LINKS和MAX_ENABLED_ROLES)。除此以外,大部分的UGA用於私有SQL區。UGA記憶體的所在依賴於會話的設定。在專用服務模式下,會話和程式是一對一的關係,UGA位於PGA中。固定UGA是PGA中的一段記憶體段,而UGA堆是PGA的子堆。在MTS模式下,固定UGA是shared pool中的一段記憶體段,而UGA堆是Large Pool的子堆,如果從large pool分配失敗,則從shared pool中分配。
MTS模式下,可以透過Profile中的PRIVATE_SGA項(透過dba_profiles檢視)來控制每個UGA佔用的SGA的總的大小,但是不建議這樣做。
Oracle9.2以後,有一個新的隱含引數:_use_realfree_heap。當設定這個引數為true時,Oracle會為CGA、UGA單獨分配堆,而不從PGA中分配。它的預設值為false,而當設定了pga_aggregate_target後,它的值自動被改為true。
四. CGA (The Call Global Area)
與其他的全域性區不同,CGA(Call Global Area呼叫全域性區)的存在是瞬間的。它只存在於一個呼叫過程中。對於例項的一些低層次的呼叫需要CGA,包括:
1)解析一條SQL語句;
2)執行一條SQL語句;
3)取一條SELECT語句的輸出值。
如果語句產生了遞迴呼叫,則需要為每個遞迴呼叫分配一個CGA。如上所述,遞迴呼叫是在語句解析、最佳化器產生語句查詢計劃、DML操作時需要查詢或修改資料字典資訊的呼叫。
無論UGA存在於PGA還是SGA,CGA都是PGA的subheap。因為無論那種模式,會話在做呼叫時總需要一個進行進行處理。這一點很重要,特別是在MTS模式下時,如果發現一次呼叫很久沒有響應,則可能需要增加PGA的大小。
當然,呼叫並不是只透過CGA中的資料結構來工作。實際上,呼叫所需要的大部分的重要資料結構都來自於UGA。例如私有SQL取和排序區都存放在UGA中,因為呼叫結束後,它們是被保留的。CGA中只包含了那些呼叫結束後可以被釋放的資料。例如,CGA中包含了直接IO快取、關於遞迴呼叫的資訊、用於表示式評估(產生查詢計劃時)的的堆空間和其他一些臨時資料。
Java呼叫記憶體也分配在CGA中。它被分為三部分空間:堆空間、新空間和老空間。在呼叫期間(呼叫長短依賴於使用期長短和大小),在新空間和老空間中的記憶體段不再使用的記憶體段將被垃圾收集器回收。
五.軟體程式碼區(Software Code Area)
軟體程式碼區是一部分用於存放那些正在執行和可以被執行的程式碼(Oracle自身的程式碼)的記憶體區。Oracle程式碼一般儲存在一個不同於使用者程式儲存區的軟體程式碼區,而使用者程式儲存區是排他的、受保護的區域。
軟體區的大小一般是固定的,只有Oracle軟體升級或重灌後才會改變。在不同作業系統下,這部分割槽域所要求的大小也不同。
軟體區是隻讀的,可以被安裝成共享的或非共享的。可能的情況下,Oracle程式碼是共享的,這樣所有Oracle使用者都可以直接訪問這些程式碼,而不需要各自儲存一份複製在自己的記憶體中。這樣可以節省大量記憶體並提高整體效能。
而使用者程式也可以是共享的或非共享的。一些Oracle工具(如SQL Plus)能被安裝成共享的,但有些不能。如果一臺機器執行多個例項,這些例項可以使用同一個Oracle程式碼區。
另外要注意的是:並不是所有作業系統都能將軟體區安裝成共享的,如Windows。
先看Oracle記憶體儲存的主要內容是什麼:
程式程式碼(PLSQL、Java);
關於已經連線的會話的資訊,包括當前所有活動和非活動會話;
程式執行時必須的相關資訊,例如查詢計劃;
Oracle程式之間共享的資訊和相互交流的資訊,例如鎖;
那些被永久儲存在外圍儲存介質上,被cache在記憶體中的資料(如redo log條目,資料塊)。
每個Oracle資料庫都是由Oracle Instance(例項)與資料庫(資料檔案,控制檔案、重做日誌檔案)組成,其中所謂例項就是使用者同資料庫互動的媒介,使用者透過於一個例項相連來運算元據庫。而例項又是由統一的記憶體結構(SGA,PGA,UGA)和一批記憶體駐留程式組成。例項在作業系統中用Oracle_SID來標識,在Oracle中用引數INSTANCE_NAME來標識, 它們兩個的值是相同的。資料庫啟動時,系統首先在伺服器記憶體中分配系統全域性區(SGA), 構成了Oracle的記憶體結構,然後啟動若干個常駐記憶體的作業系統程式,即組成了Oracle的 程式結構,記憶體區域和後臺程式合稱為一個Oracle例項。
一. 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)
其他資訊(如資料庫和例項的狀態資訊)
SQL>show sga
TotalSystem Global Area 612368384 bytes
FixedSize 1250428 bytes
VariableSize 192940932 bytes
DatabaseBuffers 411041792 bytes
RedoBuffers 7135232 bytes
SGA 中的資料字典快取 和其他資訊會被例項的後臺程式所訪問,它們在例項啟動後就固定在SGA中了,而且不會改變,所以這部分又稱為固定SGA(Fixed SGA)。這部分割槽域的大小一般小於100K。
Shared Pool、Java Pool、Large Pool和Streams Pool這幾塊記憶體區的大小是相應系統引數設定而改變的,所以有通稱為可變SGA(Variable SGA)。
透過下面的語句查詢
SQL>show parameter sga
NAME TYPE VALUE
----------------------------------------------- -------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 584M
sga_target big integer 584M
先對這幾個引數做一下說明:
SQL> select name,value ,ISSYS_MODIFIABLEfrom v$parameter where name like 'sga%';
NAME VALUE ISSYS_MOD
------------------------------ ---------
sga_max_size 612368384 FALSE
sga_target 612368384 IMMEDIATE
如果ISSYS_MODIFIABLE 返回的是false,說明該引數無法用alter system語句動態修改,需要重啟資料庫。
所以sga_max_size 是不可以動態調整的。但是我們可以對sga_target 進行動態的調整。
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 設為實體記憶體的60%,PGA 設為20%。 但是現在伺服器記憶體是相當大的。 幾百G的記憶體隨處可見。60%也就是幾百G記憶體。顯然這樣也是不合適的。 所以要根據自己系統來設定設定這個值。這個也就所說的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到相應的頁時,才會置換到實體記憶體中。我們可以透過設定PRE_PAGE_SGA引數,讓例項一啟動後,所有SGA都分配到實體記憶體。
這個引數的預設值為FALSE,即不將全部SGA置入實體記憶體中。當設定為TRUE時,例項啟動會將全部SGA置入實體記憶體中。它可以使例項啟動達到它的最大效能狀態,但是,啟動時間也會更長(因為為了使所有SGA都置入實體記憶體中,Oracle程式需要touch所有的SGA頁)。
SQL>alter system set pre_page_sga=true scope=spfile;
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 ManagementASMM)。而控制這一特性的,也就僅僅是這一個引數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一樣的值了。
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錯誤的機率。
1.1Database Buffer Cache
BufferCache是SGA區中專門用於存放從資料檔案中讀取的的資料塊複製的區域。Oracle程式如果發現需要訪問的資料塊已經在buffer cache中,就直接讀寫記憶體中的相應區域,而無需讀取資料檔案,從而大大提高效能(記憶體的讀取效率是磁碟讀取效率的14000倍)。Buffer cache對於所有Oracle程式都是共享的,即能被所有Oracle程式訪問。
和Shared Pool一樣,buffer cache被分為多個集合,這樣能夠大大降低多CPU系統中的爭用問題。
1.1.1Buffer cache的管理
Oracle對於buffer cache的管理,是透過兩個重要的連結串列實現的:寫連結串列和最近最少使用連結串列(the Least Recently Used LRU)。寫連結串列所指向的是所有髒資料塊快取(即被程式修改過,但還沒有被回寫到資料檔案中去的資料塊,此時緩衝中的資料和資料檔案中的資料不一致)。而LRU連結串列指向的是所有空閒的快取、pin住的快取以及還沒有來的及移入寫連結串列的髒快取。空閒快取中沒有任何有用的資料,隨時可以使用。而pin住的快取是當前正在被訪問的快取。LRU連結串列的兩端就分別叫做最近使用端(the Most Recently Used MRU)和最近最少使用端(LRU)。
1)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 blockgets',value,0))
+sum(decode(name,'consistentgets',value,0))))) * 100 "Hit Ratio" from v$sysstat;
Hit Ratio
----------
98.3471481
一個良好效能的系統,命中率一般保持在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程式將髒快取寫入磁碟去。
2) 全表掃描
當發生全表掃描(Full Table Scan)時,使用者程式讀取表的資料塊,並將他們放在LRU連結串列的LRU端(和上面不同,不是放在MRU端)。這樣做的目的是為了使全表掃描的資料儘快被移出。因為全表掃描一般發生的頻率較低,並且全表掃描的資料塊大部分在以後都不會被經常使用到。
而如果你希望全表掃描的資料能被cache住,使之在掃描時放在MRU端,可以透過在建立或修改表(或簇)時,指定CACHE引數。
3) FlushBuffer
回顧一下前面一個使用者程式訪問一個資料塊的過程,如果訪問的資料塊不在buffer cache中,就需要掃描LRU連結串列,當達到掃描塊數限制後還沒有找到空閒buffer,就需要通知DBW0將髒快取回寫到磁碟。如果一個系統中存在大量的髒緩衝,那麼就可能導致使用者程式訪問資料效能下降。
我們可以透過人工干預將所有髒緩衝回寫到磁碟去,這就是flush buffer。
在9i,可以用以下語句:
altersystem set events = 'immediate trace name flush_cache'; --9i
在10g,可以用以下方式(9i的方式在10g仍然有效):
altersystem flush buffer_cache; -- 10g
另外,9i的設定事件的方式可以是針對系統全部的,也可以是對會話的(即將該會話造成的髒緩衝回寫)。
1.1.2Buffer Cache的重要引數配置
1) BufferCache的大小配置
由於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,這個數對於一個系統來說一般是不夠用的。
10G中提供了自動記憶體管理,透過使用sga_target在在多個元件間自動分配記憶體以保證最有效的記憶體使用.如shared pool javapool largepool buffer cache都無需顯式設定這些元件的大小,預設都是0,當某個元件需要記憶體時,可以透過內部自動調整機制請求記憶體轉移.
注意:db_cache_size和db_block_buffers是不能同時設定的,否則例項啟動時會報錯。
SQL>alter system set db_block_buffers=16384 scope=spfile;
systemaltered.
SQL>alter system set db_cache_size=20M scope=memory;
systemaltered.
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。
2) 多種塊尺寸系統中的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引數。
3)多緩衝池
可以配置不同的buffer cache,可以達到不同的cache資料的目的。比如,可以設定一部分buffer cache快取過的資料在使用後後馬上釋放,使後來的資料可以立即使用緩衝池;還可以設定資料進入緩衝池後就被keep住不再釋放。部分資料庫物件(表、簇、索引以及分割槽)可以控制他們的資料快取的行為,而這些不同的快取行為就使用不同緩衝池。
保持緩衝池(Keep Buffer Pool)用於快取那些永久駐入記憶體的資料塊。它的大小由引數DB_KEEP_CACHE_SZIE控制;
回收緩衝池(Recycle Buffer Pool)會立即清除那些不在使用的資料快取塊。它的大小由引數DB_RECYLE_CACHE_SIZE指定;
預設的標準快取池,也就是上面所說的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之間只能設定一個。
4) 緩衝池建議器
從9i開始,Oracle提供了一些自動最佳化工具,用於調整系統配置,提高系統效能。建議器就是其中一種。建議器的作用就是在系統執行過程中,透過監視相關統計資料,給相關配置在不同情況下的效能效果,提供給DBA做決策,以選取最佳的配置。
9i中,Buffer Cache就有了相應的建議器。引數db_cache_advice用於該建議器的開關,預設值為FALSE(即關)。當設定它為TRUE後,在系統執行一段時間後,就可以查詢檢視v$db_cache_advice來決定如何使之DB_CACHE_SIZE了。關於這個建議器和檢視,我們會在下面的內容中介紹。
5) 其他相關引數
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.2 SharePool
SGA中的共享池由庫快取(Library Cache)、字典快取(Dictionary Cache)、用於並行執行訊息的緩衝以及控制結構組成。
SharedPool的大小由引數SHARED_POOL_SIZE決定。9i中,在32位系統下,這個引數的預設值是8M,而64位系統下的預設值位64M。最大為4G。 10g 以後可以透過SGA_TARGET 引數來自動調整。
對於Shared Pool的記憶體管理,是透過修正過的LRU演算法表來實現的。
1.2.1 庫快取(Library Cache)
LibraryCache中包括共享SQL區(Shared SQL Areas)、PL/SQL儲存過程以及控制結構(如鎖、庫快取控制程式碼)。
任何使用者都可以訪問共享SQL區(可以透過v$sqlarea訪問,隨後會介紹這個重要檢視)。因此庫快取存在於SGA的共享池中。
1) 共享SQL區和私有SQL區
Oracle會為每一條SQL語句執行(每執行一條語句Oracle都會開啟一個遊標)提供一個共享SQL區(Shared SQL Areas)和私有SQL區(Private SQL Areas屬於PGA)。當發現兩個(或多個)使用者都在執行同一SQL語句時,Oracle會重新組織SQL區,使這些使用者能重用共享SQL區。但他們還會在私有SQL區中儲存一份這條SQL語句的複製。
一個共享SQL區中儲存了一條語句的解析樹和查詢計劃。在多使用者系統中,Oracle透過為SQL語句使用同一共享SQL區多次執行來節省記憶體。
當一條新的SQL語句被解析時,Oracle從共享池中分配一塊記憶體來儲存共享SQL區。這塊記憶體的大小與這條語句的複雜性相關。如果Shared Pool不夠空間分配給共享SQL區,Oracle將釋放從LRU連結串列中查詢到最近最少使用的記憶體塊,直到有足夠空間給新的語句的共享SQL區。如果Oracle釋放的是一個共享SQL區的記憶體,那麼相應的語句在下次執行時需要再次解析並重新分配共享SQL區。而從解析語句到分配共享SQL區是一個比較消耗CPU的工程。這就是為什麼我們提倡使用繫結變數的原因了。在沒有使用繫結變數時,語句中的變數的數值不同,Oracle就視為一條新的語句(9i後可以透過cursor_sharing來控制),重複上面的解析、記憶體分配的動作,將大大消耗系統資源,降低系統效能。
2) PL/SQL程式單元
Oracle對於PL/SQL程式單元(儲存過程、函式、包、匿名PL/SQL塊和觸發器)的處理過程和對單個的SQL語句的處理過程相似。它會分配一個共享區來儲存被解析、編譯過的程式單元。同時分配一個私有區域來存放執行程式單元的會話所指定的程式單元的引數值(包括本地變數、全域性變數和包變數——這也叫做包的例項化)和用於執行程式所需的記憶體。如果多個使用者執行同一個程式單元,則他們共享同一個共享區域,並且各自保持一份私有區域,用於使用者會話中指定的變數值。
而一個PL/SQL程式單元中的每條單個SQL語句的處理過程則和上面描述的SQL語句的處理過程相同。要注意一點,儘管這些語句是從PL/SQL程式單元中來的,但是Oracle還是會為這些語句分配一塊共享SQL區,同時為每個使用者分配一個相應的私有SQL區。
1.2.2 字典快取(Dictionary Cache)
資料字典是有關於資料庫的參考資訊、資料庫的結構資訊和資料庫中的使用者資訊的一組表和檢視的集合,如我們常用到的V$檢視、DBA_檢視都屬於資料字典。在SQL語句解析的過程中,Oracle可以非常迅速的訪問(如果需要的話)這些資料字典,在SQL Trace中,這種對資料字典的訪問就被統計為回撥(recursive calls)。
因為Oracle對資料字典訪問如此頻繁,因此記憶體中有兩處地方被專門用於存放資料字典。一個地方就是資料字典快取(Data Dictionary Cache)。資料字典快取也被稱為行快取(Row Cache),因為它是以記錄行為單元儲存資料的,而不像Buffer Cache是以資料塊為單元儲存資料。記憶體中另外一個儲存資料字典的地方是庫快取。所有Oracle的使用者都可以訪問這兩個地方以獲取資料字典資訊。
1.2.3 共享池的記憶體管理
通常來說,共享池是根據修正過的LRU演算法來是否其中的物件(共享SQL區和資料自動記錄行)的,否則這些物件就一直保持在共享池中。如果共享池需要為一個新物件分配記憶體,並且共享池中沒有足夠記憶體時,記憶體中那些不經常使用的物件就被釋放掉。一個被許多會話使用過的共享池物件,即使最初建立它的程式已經結束,只要它是有用的,都會被修正過的LRU演算法一直保持在共享池中。這樣就使一個多使用者的Oracle系統對SQL語句的處理和記憶體消耗最小。
注意,即使一個共享SQL區與一個開啟的遊標相關,但如果它長時間沒有被使用,它還是可能會被從共享池中釋放出來。而此時如果開啟的遊標還需要執行它的相關語句,Oracle就會重新解析語句,並分配新的共享SQL區。
當一條SQL語句被提交給Oracle執行,Oracle會自動執行以下的記憶體分配步驟:
1. Oracle檢查共享池,看是否已經存在關於這條語句的共享SQL區。如果存在,這個共享SQL區就被用於執行這條語句。而如果不存在,Oracle就從共享池中分配一塊新的共享SQL區給這條語句。同時,無論共享SQL區存在與否,Oracle都會為使用者分配一塊私有SQL區以儲存這條語句相關資訊(如變數值)。
2. Oracle為會話分配一個私有SQL區。私有SQL區的所在與會話的連線方式相關。
在以下情況下,Oracle也會將共享SQL區從共享池中釋放出來:
1)當使用ANALYZE語句更新或刪除表、簇或索引的統計資訊時,所有與被分析物件相關的共享SQL區都被從共享池中釋放掉。當下一次被釋放掉的語句被執行時,又重新在一個新的共享SQL區中根據被更新過的統計資訊重新解析。
2) 當物件結構被修改過後,與該物件相關的所有共SQL區都被標識為無效(invalid)。在下一次執行語句時再重新解析語句。
3)如果資料庫的全域性資料庫名(Global Database Name)被修改了,共享池中的所有資訊都會被清空掉。
4)DBA透過手工方式清空共享池:ALTER SYSTEM FLUSH SHARED_POOL;
SharedPool能被分成幾個區域,分別被不同的latch(latch數最大為7,可以透過隱含引數_kghdsidx_count設定)保護。
表x$kghlu可以檢視shared pool中的LRU列表。當滿足以下條件之一時,shared pool會分為多個區,分別有不同的LRU連結串列管理:
1)在10g之前版本,如果shared pool大於128M、CPU數量大於4;
2)Oracle資料庫版本為10g
這時,在x$kghlu中就會對應不同記錄。
1.2.4 保留共享池
前面提到,如果Oracle解析一個 PL/SQL程式單元,也需要從共享池中分配記憶體給這些程式單元物件。由於這些物件本一般比較大(如包),所以分配的記憶體空間也相對較大。系統經過長時間執行後,共享池可能存在大量記憶體碎片,導致無法滿足對於大塊記憶體段的分配。
為了使有足夠空間快取大程式塊,Oracle專門從共享池內建出一塊區域來來分配記憶體保持這些大塊。這個保留共享池的預設大小是共享池的5%。它的大小也可以透過引數SHARED_POOL_RESERVED_SIZE來調整。保留區是從共享池中分配,不是直接從SGA中分配的,它是共享池的保留部分,用於儲存大塊段。
SharedPool中記憶體大於5000位元組的大段就會被存放在共享池的保留部分。而這個大小限制是透過隱含引數_SHARED_POOL_RESERVED_MIN_ALLOC來設定的(如前面所說,隱含引數不要去修改它)。除了在例項啟動過程中,所有小於這個數的記憶體段永遠都不會放到保留部分中,而大於這個值的大記憶體段也永遠不會存放到非保留區中,即使共享池的空間不夠用的情況下也是如此。
保留區的空閒記憶體也不會被包含在普通共享池的空閒列表中。它會維護一個單獨的空閒列表。保留池也不會在它的LRU列表中存放可重建(Recreatable關於記憶體段的各種狀態我們在後面的內容中再介紹)段。當釋放普通共享池空閒列表上的記憶體時是不會清除這些大段的,同樣,在釋放保留池的空閒列表上的大記憶體段時也不會清除普通共享池中記憶體。
透過檢視V$SHARED_POOL_RESERVED可以查到保留池的統計資訊。其中欄位REQUEST_MISSES記錄了沒有立即從空閒列表中得到可用的大記憶體段請求次數。這個值要為0。因為保留區必須要有足夠個空閒記憶體來適應那些短期的記憶體請求,而無需將那些需要長期cache住的沒被pin住的可重建的段清除。否則就需要考慮增大SHARED_POOL_RESERVED_SIZE了。
可以透過觀察檢視V$SHARED_POOL_RESERVED的MAX_USED_SPACE欄位來判斷保留池的大小是否合適。大多數情況下,你會觀察到保留池是很少被使用的,也就是說5%的保留池空間可能有些浪費。但這需要經過長期觀察來決定是否需要調整保留池大小。
保留區使用shared pool的LRU連結串列來管理記憶體塊,但是在做掃描時,相互是不受影響的。例如,記憶體管理器掃描shared pool的LRU連結串列,清出空間以分配給一個小於5000位元組的記憶體請求,是不會清出保留區的記憶體塊的,相反亦然。
1.2.5 將重要、常用物件保持(Keep)在共享池中
根據LRU演算法,一些一段時間沒有使用到的記憶體塊會被情況釋放。這就可能導致一些重要的物件(如一個含有大量通用演算法函式的包、被cache的序列)被從記憶體中清除掉。這些物件可能只是間歇被使用,但是因為他們的處理過程複雜(不僅包本身重新分配記憶體、解析,還要檢查裡面的所有語句),要在記憶體中重建他們的代價非常大。
我們可以透過呼叫儲存過程DBMS_SHARED_POOL.KEEP將這些物件保持在共享池中來降低這種風險。這個儲存過程立即將物件及其從事物件載入library cache中,並將他們都標記為保持(Keeping)狀態。對於這種物件,我們建議在例項啟動時就Keep住,以減少記憶體碎片的機率。
有一種觀點認為那些大物件(如包)是沒有必要被Keep住的,因為他們會被保持在共享池的保留區(如前所述,這個區通常使用率很低),所以一般不可能被清出。這個觀點是錯誤滴!因為大多數大物件實際上是被分為多個小的記憶體段被載入共享池的,因此根本不會因為物件的大小而受到特別的保護。
另外,也不要透過頻繁呼叫某些物件以防止他們被從共享池中清出。如果共享池大小設定合理,在系統執行的高峰時期,LRU連結串列會相對較短,那些沒有被pin住的物件會很快被清出,除非他們被keep住了。
1.2.6 關於Shared Pool的重要引數
1) SHARED_POOL_SIZE
它指定了Shared Pool的大小。9i下,在32位系統中,這個引數的預設值是8M,而64位系統中的預設值位64M。
但是,在SGA中還存在一塊叫內部SGA消耗(Internal SGA Overhead)的記憶體被放置在共享池中。在9i及之前版本,共享池的統計大小(透過v$sgastat檢視統計)為SHARED_POOL_SIZE +內部SGA消耗大小。而10g以後,SHARED_POOL_SIZE就已經包含了這部分記憶體大小。因此在10g中,共享池的實際使用大小就是SHARED_POOL_SIZE -內部SGA消耗大小,這在配置共享池大小時需要考慮進去,否則,扶過SHARED_POOL_SIZE設定過小,在例項啟動時就會報ORA-00371錯誤。
2) SHARED_POOL_RESERVED_SIZE
這個引數前面已經提到,指定了共享池中快取大記憶體物件的保留區的大小。這裡不再贅述。
3) _SHARED_POOL_RESERVED_MIN_ALLOC
這個引數前面也已經介紹,設定了進入保留區的物件大小的閥值。
1.3 重做日誌快取(Redo Log Buffer)
Redo LogBuffer是SGA中一段儲存資料庫修改資訊的快取。這些資訊被儲存在重做條目(Redo Entry)中.重做條目中包含了由於INSERT、UPDATE、DELETE、CREATE、ALTER或DROP所做的修改操作而需要對資料庫重新組織或重做的必須資訊。在必要時,重做條目還可以用於資料庫恢復。
重做條目是Oracle資料庫程式從使用者記憶體中複製到Redo Log Buffer中去的。重做條目在記憶體中是連續相連的。後臺程式LGWR負責將Redo Log Buffer中的資訊寫入到磁碟上活動的重做日誌檔案(Redo Log File)或檔案組中去的。
引數LOG_BUFFER決定了Redo Log Buffer的大小。它的預設值是512K(一般這個大小都是足夠的),最大可以到4G。10g中可透過引數自動設定。當系統中存在很多的大事務或者事務數量非常多時,可能會導致日誌檔案IO增加,降低效能。這時就可以考慮增加LOG_BUFFER。
但是,Redo Log Buffer的實際大小並不是LOB_BUFFER的設定大小。為了保護Redo Log Buffer,Oracle為它增加了保護頁(一般為11K):
SQL>select * from v$sgastat where name = 'log_buffer';
POOL NAME BYTES
-------------------------------------- ----------
log_buffer 7135232
SQL>show parameter log_buffer
NAME TYPE VALUE
----------------------------------------------- ----------------
log_buffer integer 7024640
SQL>
1.4 大池(large pool)
大池是SGA中的一塊可選記憶體池,根據需要時配置。在以下情況下需要配置大池:
1) 用於共享服務(Shared Server MTS方式中)的會話記憶體和Oracle分散式事務處理的Oracle XA介面
2) 使用並行查詢(Parallel Query Option PQO)時
3) IO服務程式
4) Oracle備份和恢復操作(啟用了RMAN時)
透過從大池中分配會話記憶體給共享服務、Oracle XA或並行查詢,Oracle可以使用共享池主要來快取共享SQL,以防止由於共享SQL快取收縮導致的效能消耗。此外,為Oracle備份和恢復操作、IO服務程式和並行查詢分配的記憶體一般都是幾百K,這麼大的記憶體段從大池比從共享池更容易分配得到。
引數LARGE_POOL_SIZE設定大池的大小。大池是屬於SGA的可變區(Variable Area)的,它不屬於共享池。對於大池的訪問,是受到large memory latch保護的。大池中只有兩種記憶體段:空閒(free)和可空閒(freeable)記憶體段。它沒有可重建(recreatable)記憶體段,因此也不用LRU連結串列來管理(這和其他記憶體區的管理不同)。大池最大大小為4G。
為了防止大池中產生碎片,隱含引數_LARGE_POOL_MIN_ALLOC設定了大池中記憶體段的最小大小,預設值是16K(同樣,不建議修改隱含引數)。
此外,large pool是沒有LRU連結串列的。
1. 5 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來查詢。如何藉助建議器調整Java池的方法和使用Buffer Cache建議器類似,可以參考Buffer Cache中關於建議器部分。
1.6 流池(Streams Pool)
流池是Oracle 10g中新增加的。是為了增加對流的支援。
流池也是可選記憶體區,屬於SGA中的可變區。它的大小可以透過引數STREAMS_POOL_SIZE來指定。如果沒有被指定,Oracle會在第一次使用流時自動建立。如果設定了SGA_TARGET引數,Oracle會從SGA中分配記憶體給流池;如果沒有指定SGA_TARGET,則從buffer cache中轉換一部分記憶體過來給流池。轉換的大小是共享池大小的10%。
Oracle同樣為流池提供了一個建議器——流池建議器。建議器的統計資料可以透過檢視V$STREAMS_POOL_ADVICE查詢。使用方法參看Buffer Cache中關於最佳化器部分。
二. PGA
PGA(Program Global Area程式全域性區)是一塊包含一個服務程式的資料和控制資訊的記憶體區域。它是Oracle在一個服務程式啟動是建立的,是非共享的。一個Oracle程式擁有一個PGA記憶體區。一個PGA也只能被擁有它的那個服務程式所訪問,只有這個程式中的Oracle程式碼才能讀寫它。因此,PGA中的結構是不需要Latch保護的。
我們可以設定所有服務程式的PGA記憶體總數受到例項分配的總體PGA(Aggregated PGA)限制。
在專有伺服器(Dedicated Server)模式下,Oracle會為每個會話啟動一個Oracle程式;而在多執行緒服務(Multi-Thread Server MTS)模式下,由多個會話共享通一個Oracle服務程式。
PGA中包含了關於程式使用到的作業系統資源的資訊,以及一些關於程式狀態的資訊。而關於程式使用的Oracle共享資源的資訊則是在SGA中。這樣做可以使在程式以外中止時,能夠及時釋放和清除這些資源。
?StackSpace是用來儲存使用者會話變數和陣列的儲存區域;
?UserSession Data是為使用者會話使用的附加儲存區。
|--SessionInformation
|--SortArea
|--CursorInformation
注意Session information(使用者會話資訊)在獨佔伺服器中與在共享伺服器中所處的記憶體區域是不同的。
2.1 PGA的組成
PGA由兩組區域組成:固定PGA和可變PGA(或者叫PGA堆,PGA Heap【堆——Heap就是一個受管理的記憶體區】)。固定PGA和固定SGA類似,它的大小時固定的,包含了大量原子變數、小的資料結構和指向可變PGA的指標。
可變PGA是一個記憶體堆。它的記憶體段可以透過檢視X$KSMPP(另外一個檢視X$KSMSP可以查到可變SGA的記憶體段資訊,他們的結構相同)查到。PGA堆包含用於存放X$表的的記憶體(依賴與引數設定,包括DB_FILES、CONTROL_FILES)。
總的來說,PGA的可變區中主要分為以下三部分內容:
1)私有SQL區;
2)遊標和SQL區
3)會話記憶體
2.1.1 私有SQL區(Private SQL Area)
私有SQL區包含了繫結變數值和執行時期記憶體結構資訊等資料。每一個執行SQL語句的會話都有一個塊私有SQL區。所有提交了相同SQL語句的使用者都有各自的私有SQL區,並且他們共享一個共享SQL區。因此,一個共享SQL區可能和多個私有共享區相關聯。
一個遊標的私有SQL區又分為兩個生命週期不同的區:
永久區:包含繫結變數資訊。當遊標關閉時被釋放。
執行區:當執行結束時釋放。
建立執行區是一次執行請求的第一步。對於INSERT、UPDATE和DELETE語句,Oracle在語句執行結束時釋放執行區。對於查詢操作,Oracle只有在所有記錄被fetch到或者查詢被取消時釋放執行區。
2.1.2 遊標和SQL區(Cursors and SQL Areas)
一個Oracle預編譯程式或OCI程式的應用開發人員能夠很明確的開啟一個遊標,或者控制一塊特定的私有SQL區,將他們作為程式執行的命名資源。另外,Oracle隱含的為一些SQL語句產生的遞迴呼叫(前面有介紹,讀取資料字典資訊)也使用共享SQL區。
私有SQL區是由使用者程式管理的。如何分配和釋放私有SQL區極大的依賴與你所使用的應用工具。而使用者程式可以分配的私有SQL區的數量是由引數OPEN_CURSORS控制的,它的預設值是50。
在遊標關閉前或者語句控制程式碼被釋放前,私有SQL區將一直存在(但其中的執行區是在語句執行結束時被釋放,只有永久區一直存在)下去。應用開發人員可以透過將所有開啟的不再使用的遊標都關閉來釋放永久區,以減少使用者程式所佔用的記憶體。
2.1.3 會話記憶體(Session Memory)
會話記憶體是一段用於儲存會話變數(如登入資訊)和其他預會話相關資訊的記憶體。對於共享伺服器模式下,會話記憶體是共享的,而不是私有的。
對於複雜的查詢(如決策支援系統中的查詢),執行區的很大一部分被那些記憶體需求很大的操作分配給SQL工作區(SQL Work Area)。這些操作包括:
基於排序的操作(ORDER BY、GROUP BY、ROLLUP、視窗函式);
Hash Join
Bitmapmerge
Bitmapcreate
例如,一個排序操作使用工作區(這時也可叫排序區Sort Area)來將一部分資料行在記憶體排序;而一個Hash Join操作則使用工作區(這時也可以叫做Hash區 Hash Area)來建立Hash表。如果這兩種操作所處理的資料量比工作區大,那就會將輸入的資料分成一些更小的資料片,使一些資料片能夠在記憶體中處理,而其他的就在臨時表空間的磁碟上稍後處理。儘管工作區太小時,Bitmap操作不會將資料放到磁碟上處理,但是他們的複雜性是和工作區大小成反比的。因此,總的來說,工作區越大,這些操作就執行越快。
工作區的大小是可以調整的。一般來說,大的工作區能讓一些特定的操作效能更佳,但也會消耗更多的記憶體。工作區的大小足夠適應輸入的資料和相關的SQL操作所需的輔助的記憶體就是最優的。如果不滿足,因為需要將一部分資料放到臨時表空間磁碟上處理,操作的響應時間會增長。
2.2 PGA記憶體自動管理
SQL工作區可以是自動的、全域性的管理。DBA只要設定引數PGA_AGGREGATE_TARGET給一個例項的PGA記憶體指定總的大小。設定這個引數後,Oracle將它作為一個總的全侷限制值,儘量使所有Oracle服務程式的PGA記憶體總數不超過這個值。
在這個引數出現之前,DBA要調整引數SORT_AREA_SIZE、 HASH_AREA_SIZE,、BITMAP_MERGE_AREA_SIZE和CREATE_BITMAP_AREA_SIZE(關於這些引數,我們會在後面介紹),使效能和PGA記憶體消耗最佳。對這些引數的調整是非常麻煩的,因為即要考慮所有相關的操作,使工作區適合它們輸入資料大小,又要使PGA記憶體不消耗過大導致系統整體效能下降。
9i以後,透過設定了引數PGA_AGGREGATE_TARGET,使所有會話的工作區的大小都是自動分配。同時,所有*_AREA_SIZE引數都會失效。在任何時候,例項中可用於工作區的PGA記憶體總數都是基於引數PGA_AGGREGATE_TARGET的。工作區記憶體總數等於引數PGA_AGGREGATE_TARGET的值減去系統其他元件(如分配給會話的PGA記憶體)的記憶體消耗。分配給Oracle程式的PGA記憶體大小是根據它們對記憶體的需求情況來的。
引數WORKAREA_SIZE_POLICY決定是否使用PGA_AGGREGATE_TARGET來管理PGA記憶體。它有兩個值:AUTO和MANUAL。預設是AUTO,即使用PGA_AGGREGATE_TARGET來管理PGA記憶體。其實,從引數WORKAREA_SIZE_POLICY的名字上可以看出,Oracle的PGA記憶體自動管理只會調整工作區部分,而非工作區部分(固定PGA區)則不會受影響。
還有注意一點就是:10g之前,PGA_AGGREGATE_TARGET只在專用服務模式下生效。而10g以後,PGA記憶體自動管理在專有服務模式(Dedicated Server)和MTS下都有效。另外,9i在OpenVMS系統上還不支援PGA記憶體自動管理,但10g支援。
設定了PGA_AGGREGATE_TARGET以後,每個程式PGA記憶體的大小也是受限制的:
序列操作時,每個程式可用的PGA記憶體為MIN(PGA_AGGREGATE_TARGET * 5%,_pga_max_size/2),其中隱含引數_pga_max_size的預設值是200M,同樣不建議修改它。
並行操作時,並行語句可用的PGA記憶體為PGA_AGGREGATE_TARGET * 30% / DOP(Degree Of Parallelism並行度)。
2.3 專有服務(Dedicated Server)和共享服務(Shared Server)
對PGA記憶體的管理和分配,很大程度上依賴與服務模式。下面這張表顯示了在不同模式下,PGA記憶體不同部分的分配的異同:
記憶體區
專有服務
共享服務
會話記憶體
私有的
共享的
永久區所在區域
PGA
SGA
SELECT語句的執行區所在區域
PGA
PGA
DML/DDL語句的執行區所在區域
PGA
PGA
三. UGA (The User Global Area)
PGA是一段包含一個Oracle服務或後臺程式的資料和控制資訊的記憶體。PGA的大小依賴與系統的配置。在專用服務(Dedicated Server)模式下,一個服務程式與一個使用者程式相關,PGA就包括了堆空間和UGA。而UGA(User Global Area使用者全域性區)由使用者會話資料、遊標狀態和索引區組成。在共享服務(MTS)模式下,一個共享服務程式被多個使用者程式共享,此時UGA是Shared Pool或Large Pool的一部分(依賴與配置)。
許多DBA都不理解PGA和UGA之間的區別。其實這種區別可以簡單的理解為程式和會話直接的區別。在專用服務模式下,程式和會話是一對一的;而在MTS模式下,程式和會話是一對多的關係。PGA是服務於程式的,它包含的是程式的資訊;而UGA是服務於會話的,它包含的是會話的資訊。因此,MTS模式下,PGA和UGA之間的關係也是一對多的。
UGA中包含了一個會話的資訊,包括:
1)開啟遊標的永久區和執行區;
2)包的狀態資訊,特別是包的變數;
3)Java會話的資訊;
4)啟用的角色;
5)啟用的跟蹤事件(ALTER SESSION SET EVENT …);
6)起作用的NLS引數(SELECT * FROM NLS_SESSION_PARAMETERS;);
7)所有開啟的db link;
8)會話對於信任的Oracle的託管訪問標記(mandatory access control (MAC)
和PGA一樣,UGA也由兩組區組成,固定UGA和可變UGA(或者說UGA堆)。固定UGA包含了大概70個原子變數、小的資料結構以及指向UGA堆的指標。
UGA heap中的段可以透過表X$KSMUP查到(它的結構和X$KSMSP相同)。UGA堆包含了儲存一些固定表(X$表)的永久記憶體(依賴與特定引數的設定,如OPEN_CURSORS,OPEN_LINKS和MAX_ENABLED_ROLES)。除此以外,大部分的UGA用於私有SQL區。UGA記憶體的所在依賴於會話的設定。在專用服務模式下,會話和程式是一對一的關係,UGA位於PGA中。固定UGA是PGA中的一段記憶體段,而UGA堆是PGA的子堆。在MTS模式下,固定UGA是shared pool中的一段記憶體段,而UGA堆是Large Pool的子堆,如果從large pool分配失敗,則從shared pool中分配。
MTS模式下,可以透過Profile中的PRIVATE_SGA項(透過dba_profiles檢視)來控制每個UGA佔用的SGA的總的大小,但是不建議這樣做。
Oracle9.2以後,有一個新的隱含引數:_use_realfree_heap。當設定這個引數為true時,Oracle會為CGA、UGA單獨分配堆,而不從PGA中分配。它的預設值為false,而當設定了pga_aggregate_target後,它的值自動被改為true。
四. CGA (The Call Global Area)
與其他的全域性區不同,CGA(Call Global Area呼叫全域性區)的存在是瞬間的。它只存在於一個呼叫過程中。對於例項的一些低層次的呼叫需要CGA,包括:
1)解析一條SQL語句;
2)執行一條SQL語句;
3)取一條SELECT語句的輸出值。
如果語句產生了遞迴呼叫,則需要為每個遞迴呼叫分配一個CGA。如上所述,遞迴呼叫是在語句解析、最佳化器產生語句查詢計劃、DML操作時需要查詢或修改資料字典資訊的呼叫。
無論UGA存在於PGA還是SGA,CGA都是PGA的subheap。因為無論那種模式,會話在做呼叫時總需要一個進行進行處理。這一點很重要,特別是在MTS模式下時,如果發現一次呼叫很久沒有響應,則可能需要增加PGA的大小。
當然,呼叫並不是只透過CGA中的資料結構來工作。實際上,呼叫所需要的大部分的重要資料結構都來自於UGA。例如私有SQL取和排序區都存放在UGA中,因為呼叫結束後,它們是被保留的。CGA中只包含了那些呼叫結束後可以被釋放的資料。例如,CGA中包含了直接IO快取、關於遞迴呼叫的資訊、用於表示式評估(產生查詢計劃時)的的堆空間和其他一些臨時資料。
Java呼叫記憶體也分配在CGA中。它被分為三部分空間:堆空間、新空間和老空間。在呼叫期間(呼叫長短依賴於使用期長短和大小),在新空間和老空間中的記憶體段不再使用的記憶體段將被垃圾收集器回收。
五.軟體程式碼區(Software Code Area)
軟體程式碼區是一部分用於存放那些正在執行和可以被執行的程式碼(Oracle自身的程式碼)的記憶體區。Oracle程式碼一般儲存在一個不同於使用者程式儲存區的軟體程式碼區,而使用者程式儲存區是排他的、受保護的區域。
軟體區的大小一般是固定的,只有Oracle軟體升級或重灌後才會改變。在不同作業系統下,這部分割槽域所要求的大小也不同。
軟體區是隻讀的,可以被安裝成共享的或非共享的。可能的情況下,Oracle程式碼是共享的,這樣所有Oracle使用者都可以直接訪問這些程式碼,而不需要各自儲存一份複製在自己的記憶體中。這樣可以節省大量記憶體並提高整體效能。
而使用者程式也可以是共享的或非共享的。一些Oracle工具(如SQL Plus)能被安裝成共享的,但有些不能。如果一臺機器執行多個例項,這些例項可以使用同一個Oracle程式碼區。
另外要注意的是:並不是所有作業系統都能將軟體區安裝成共享的,如Windows。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29812844/viewspace-1988845/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL整體架構與記憶體結構MySql架構記憶體
- 【記憶體管理】Oracle AMM自動記憶體管理詳解記憶體Oracle
- oracle 記憶體結構具體解釋Oracle記憶體
- ORACLE記憶體管理 之一 ORACLE PGAOracle記憶體
- 4.3. Oracle整體架構Oracle架構
- oracle for aix記憶體調整OracleAI記憶體
- c 結構體記憶體對齊詳解結構體記憶體
- JVM之記憶體結構詳解JVM記憶體
- 【DATAGUARD】Oracle Dataguard體系架構詳解Oracle架構
- Oracle Golden Gate體系架構詳解OracleGo架構
- Oracle記憶體分配與調整Oracle記憶體
- oracle記憶體調整相關Oracle記憶體
- Oracle記憶體體系結構Oracle記憶體
- oracle 記憶體結構Oracle記憶體
- Oracle記憶體結構:ProcessMemory的詳細資訊Oracle記憶體SSM
- Docker整體架構Docker架構
- nginx 整體架構Nginx架構
- MSSQL記憶體架構及管理SQL記憶體架構
- 堆記憶體和棧記憶體詳解(轉載)記憶體
- JVM系列之Java記憶體結構詳解JVMJava記憶體
- Oracle體系結構之-記憶體結構Oracle記憶體
- oracle 記憶體結構(二)Oracle記憶體
- Oracle 記憶體結構(一)Oracle記憶體
- ORACLE 記憶體結構理解.Oracle記憶體
- C語言-記憶體管理之一[記憶體分配]C語言記憶體
- JVM記憶體模型詳解JVM記憶體模型
- JVM堆記憶體詳解JVM記憶體
- iOS記憶體管理詳解iOS記憶體
- Java記憶體管理原理及記憶體區域詳解Java記憶體
- Oracle - 資料庫的記憶體調整Oracle資料庫記憶體
- 《高效能MySQL》筆記-整體架構MySql筆記架構
- Dubbo框架————整體架構框架架構
- 專案-整體架構架構
- 記憶體管理之一 (轉)記憶體
- 詳細瞭解 InnoDB 記憶體結構及其原理記憶體
- Oracle記憶體引數調優技術詳解Oracle記憶體
- Oracle記憶體結構(三)----Process Memory的詳細資訊Oracle記憶體
- 8張圖瞭解JAVA整體構架知識體系!Java