[Oracle10G新特性]_17.自動共享內存管理
?
??? 關于Orace的內存管理,之前了解過一些基礎的概念,但是對于具體如何分配這個層面,倒還真是沒有了解過,這篇文章只是簡單得講了一下SGA的分配,不過對于理解Oracle的內部工作機制還是很有益處的。至少可以知道如何啟用或者關閉Oracle的自動內存管理。
?
??? 不過對于這方面的需求,關鍵還是要知道到底各個pool的工作機制,以及實際所需要的大小預計是多少,這些才是關鍵所在,需要有深刻的理解才可以。
?
------------------------------------------------------------------------------
?
自動共享內存管理
?
是不是很難準確地分配不同的池所需的內存數?自動共享內存管理特性使得自動將內存分配到最需要的地方去成為可能。
?
??? 無論您是一個剛入門的 DBA 還是一個經驗豐富的 DBA,您肯定至少看到過一次類似以下的錯誤:
?
ORA-04031:unable to allocate 2216 bytes of shared memory ("shared pool"... ...
?
??? 或者這種錯誤:
?
ORA-04031:unable to allocate XXXX bytes of shared memory
("large pool","unknown object","session heap","frame")
?
??? 或者可能這種錯誤:
?
ORA-04031:unable to allocate bytes of shared memory ("shared pool",
"unknown object","joxlod:init h", "JOX:ioc_allocate_pal")
?
??? 第一種錯誤的原因很明顯:分配給共享池的內存不足以滿足用戶請求。(在某些情況下,原因可能不是池本身的大小,而是未使用綁定變量導致的過多分析造成的碎片,這是我很喜歡的一個主題;但目前讓我們把重點放在手頭的問題上。)其它的錯誤分別來自大型池和 Java 池的空間不足。
?
??? 您需要解決這些錯誤情況,而不作任何與應用程序相關的修改。那么有哪些方案可選呢?問題是如何在 Oracle 例程所需的所有池之間劃分可用的內存。
?
?
餡餅怎么分?
?
??? 正如您所了解的,一個 Oracle 例程的系統全局區域 (SGA) 包含幾個內存區域(包括緩沖高速緩存、共享池、Java 池、大型池和重做日志緩沖)。這些池在操作系統的內存空間中占據了固定的內存數;它們的大小由 DBA 在初始化參數文件中指定。
?
??? 這四個池(數據庫塊緩沖高速緩存、共享池、Java 池和大型池)幾乎占據了 SGA 中所有的空間。(與其它區域相比,重做日志緩沖沒有占據多少空間,對我們這里的討論無關緊要。)作為 DBA,您必須確保它們各自的內存分配是充足的。
?
??? 假定您決定了這些池的值分別是 2GB、1GB、1GB 和 1GB。您將設置以下初始化參數來為數據庫例程規定池的大小。
?
db_cache_size = 2g
shared_pool_size = 1g
large_pool_size = 1g
java_pool_size = 1g
?
??? 現在,仔細看一下這些參數。坦白講,這些值是否準確?
?
??? 我相信您一定會有疑慮。在實際中,沒有人能夠為這些池指定確切的內存數 — 它們太依賴于數據庫內部的處理,而處理的特性隨時在變化。
?
??? 下面是一個示例場景。假定您有一個典型的、大部分屬于 OLTP 的數據庫,并且為緩沖高速緩存分配的專用內存比為純 OLTP 數據庫(現在已經很少見了)分配的要少。有一天,您的用戶放開了一些非常大的全表掃描,以創建當天的結束報表。Oracle9i 數據庫為您提供了在線修改內存分配的功能,但由于提供的總物理內存有限,您決定從大型池和 Java 池中取出一些內存:
?
alter system set db_cache_size = 3g scope=memory;
alter system set large_pool_size = 512m scope=memory;
alter system set java_pool_size = 512m scope=memory;
?
??? 這個解決方案能夠很好地工作一段時間,但是接著夜間的 RMAN 作業(它們使用大型池)開始了,大型池將立即出現內存不足。同樣,您從數據庫高速緩存中取出一些內存來補充大型池,以挽救這種局面。
?
??? RMAN 作業完成,然后啟動一個廣泛使用 Java 的批處理程序,接著您開始看到與 Java 池相關的錯誤。因此,您(再次)重新分配池,以滿足 Java 池和數據庫高速緩存上的內存需求:
?
alter system set db_cache_size = 2G scope=memory;
alter system set large_pool_size = 512M scope=memory;
alter system set java_pool_size = 1.5G scope=memory;
?
??? 第二天早上,OLTP 作業恢復在線,這個循環又完全重復!
?
??? 解決這種惡性循環的一種替代方法是永久設置每個池的最大需求。不過,這么做的話,您分配的總的 SGA 可能超出可用的內存 — 從而在為每個池分配的內存數不足時,將增加交換和分頁的風險。人工重新分配的方法(雖然不實際)目前看起來很不錯。
?
??? 另一種替代方法是將值設為可接受的最小值。不過,當需求增長且內存不能完全滿足時,性能將受到影響。
?
??? 注意在所有這些示例中,分配給 SGA 的總內存保持不變,而池之間的內存分配根據即時的需求進行修改。如果 RDBMS 將自動探測來自用戶的需求并相應地重新分布內存分配,那不是很好嗎?
?
??? Oracle 數據庫 10g 中的自動共享內存管理特性正好能夠實現這一目的。您可以決定 SGA 的總大小,然后設置一個名稱為 SGA_TARGET 的參數,這個參數決定 SGA 的總大小。SGA 內部的各個池將根據工作負載動態地進行配置。實現自動內存分配僅僅需要 SGA_TARGET 參數的一個非零值。
?
?
設置自動共享內存管理
?
??? 讓我們看看該特性是如何工作的。首先,確定 SGA 的總大小。您可以通過確定現在分配了多少內存來估計這個值。
?
SQL> select sum(value)/1024/1024 from v$sga;
?
SUM(VALUE)/1024/1024
--------------------
???????????????? 500
?
??? 此時 SGA 的當前總大小近似為 500MB,并且這個值將變為 SGA_TARGET 的值。接下來,執行語句:
?
alter system set sga_target = 500M scope=both;
?
??? 這種方法不需要為各個池設置不同值;因而,您將需要在參數文件中使它們的值為零或全部刪除它們。
?
shared_pool_size = 0
large_pool_size = 0
java_pool_size = 0
db_cache_size = 0????
?
??? 再循環數據庫,使這些值生效。
?
??? 這個人工過程還可以通過 Enterprise Manager 10g 實施。從數據庫主頁中,選擇 "Administration" 選項卡,然后選擇 "Memory Parameters"。對于人工配置的內存參數,將顯示標記為 "Enable" 的按鈕,以及所有人工配置的池的值。單擊 "Enable" 按鈕,啟用自動共享內存管理特性。企業管理器將完成剩下的工作。
?
??? 在配置了自動內存分配之后,您可以利用以下命令檢查它們的大小:
?
SQL> select current_size from v$buffer_pool;
?
CURRENT_SIZE
------------
???????? 340
?
SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;
?
POOL???????????? MBYTES
------------ ----------
java pool???????????? 4
large pool??????????? 4
shared pool???????? 148
?
??? 正如您所看到的,所有的池都從 500MB 的總目標大小中自動進行分配。(參見圖 1。)緩沖高速緩存大小是 340MB,Java 池是 4MB,大型池是 4MB,共享池是 148MB。它們合起來總的大小為 (340+4+4+148=) 496MB,近似與 500MB 的目標 SGA 的大小相同。
?
圖 1:池的初始分配
?
??? 現在假定提供給 Oracle 的主機內存從 500MB 減少為 300MB,這意味著我們必須減少總 SGA 的大小。我們可以通過減小目標 SGA 大小來反映這種變化。
?
alter system set sga_target = 300M scope=both;
?
??? 現在查看各個池,我們可以看到:
?
SQL> select current_size from v$buffer_pool;
?
CURRENT_SIZE
------------
???????? 244
?
SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;
?
POOL???????????? MBYTES
------------ ----------
java pool???????????? 4
large pool??????????? 4
shared pool????????? 44
?
??? 占用的總大小是 240+4+4+44 = 296MB,接近于目標的 300MB。注意如圖 2 所示,當 SGA_TARGET 改變時,如何自動重新分配池。
?
圖 2:在將 SGA 大小減少到 300MB 之后重新分配池
?
??? 這些池的大小是動態的。池將根據工作負載擴展,以容納需求的增長,或縮小以容納另一個池的擴展。這種擴展或縮小自動發生,無需 DBA 的干預,這與本文開頭的示例不同。讓我們暫時返回到那個場景,假定在初始分配后,RMAN 作業啟動,指示需要一個更大的大型池,大型池將從 4MB 擴展到 40MB,以容納需求。這個額外的 36MB 將從數據庫緩沖中劃出,數據庫塊緩沖將縮小,如圖 3 所示。
?
圖 3:在對大型池的需求增長之后經過重新分配的池
?
??? 池的大小變化基于系統上的工作負載,因此不需要為最壞的情況調整池的大小 — 它們將根據需求的增長自動調整。此外,SGA 的總大小始終在由 SGA_TARGET 指定的最大值之內,因此不存在使內存需求的增長比例失調(這將導致分頁和交換)的風險。您可以動態地將 SGA_TARGET 增加至絕對最大值,這個絕對最大值是通過調整參數 SGA_MAX_SIZE 指定的。
?
哪些池不受影響?
?
??? SGA 中的一些池不受動態大小調整的影響,但是必須顯式指定這些池。其中值得注意的是非標準塊大小的緩沖池,以及 KEEP 池或 RECYCLE 池的非默認塊大小。如果您的數據庫有一個塊大小為 8K,而您想要配置 2K、4K、16K 和 32K 塊大小的池,那么您必須手動設置它們。它們的大小將保持不變;它們將不會根據負載縮小或擴展。當使用多種大小的緩沖池、KEEP 池和 RECYCLE 池時,您應當考慮這個因素。此外,日志緩沖不受內存調整的影響 — 不管工作負載如何,在參數 log_buffer 中設定的值是不變的。( 在 10g 中,還可以在 SGA 中定義一種新的池:流池 (stream pool),它用參數 streams_pool_size 進行設置。該池也不受自動內存調整的影響。)
?
??? 這就產生了一個有趣的問題。如果您需要一個非默認塊大小的池,而且想自動管理其它的池,那么該怎么辦?
?
??? 如果您指定了這些非自動調整的參數中的任意一個(如 db_2k_cache_size),那么它們的總大小將從 SGA_TARGET 值中減去,以計算自動調整的參數值,以使 SGA 的總大小保持不變。例如,假設值看起來像這樣:
?
sga_target = 500M
db_2k_cache_size = 50M
?
??? 其余的池參數未設置。50MB 的 2KB 緩沖池為自動調整的池(如默認塊大小緩沖池 (db_cache_size)、共享池、Java 池和大型池)保留了 450MB。當以一種方法動態地調整不可自動調整的參數(如 2KB 塊大小池)——這種方法將影響到可自動調整部分的大小,可自動調整的部分將重新調整。例如,將 db_2k_cache_size 的值從 50MB 提高到 100MB 只為可自動調整的參數剩余 400MB。因此,如圖 4 所示,可調整的池(如共享池、大型池、Java 池和默認緩沖池)自動縮小,以將它們的總大小從 450MB 減少到 400MB。
?
圖 4:配置非自動緩沖參數的效果
??? 但如果您有足夠的可用內存,或者上述風險可能不是那么明顯,那應該怎么辦?如果這樣的話,您可以通過不指定參數文件中的參數 SGA_TARGET、通過在文件中將其設為 0,或者通過使用 ALTER SYSTEM 動態地將其修改為 0 來關閉自動大小調整。當 SGA_TARGET 被設為 0 時,池的當前值被自動設為它們的參數。
?
使用 Enterprise Manager
??? 您還可以使用 Enterprise Manager 10g 來處理這些參數。從數據庫主頁中單擊超鏈接 "Memory Parameters",這將顯示一個類似于圖 5 中的屏幕。
?
圖 5:在 Enterprise Manager 中調整自動共享內存管理
?
??? 注意紅圈中的項目:數據庫在 Automatic Shared Memory Management 模式下運行,總大小為 564MB — 與在參數 SGA_TARGET 中指定的值相同。您可以在此修改它,然后單擊 Apply 按鈕接受這些值;可調整的參數將自動調整。
?
為每個池指定一個最小值
?
??? 假定您將 SGA_TARGET 設為 600MB,并且各個池已自動分配:
?
????
池??? 大小(MB)?
??? 緩沖池 ?? 404
??? Java池 ??? 4
??? 大型池 ???? 4
??? 共享池 ?? 148
?
??? 看看上述值,您可能推斷 4MB 的 Java 池和大型池可能有點不足;這個值在運行時無疑需要增加。因此,您可能想確保這些池至少在最初時具有更高的值,比如說,分別為 8MB 和 16MB。您可以通過在參數文件中顯式地指定這些池的值或動態使用 ALTER SYSTEM 來實現這一目的(如下所示)。
?
alter system set large_pool_size = 16M;
alter system set java_pool_size = 8M;
?
??? 現在查看這些池,您可以看到:
?
SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;
?
POOL???????????? MBYTES
------------ ----------
java pool???????????? 8
large pool?????????? 16
shared pool???????? 148
?
SQL> select current_size from v$buffer_pool;
?
CURRENT_SIZE
------------
???????? 388
?
??? 池的重新分配顯示如下:
?
????
池???? 大小(MB)?
??? 緩沖池 ??? 388
??? Java池 ????? 8
??? 大型池 ???? 16
??? 共享池 ??? 148
?
??? 注意 Java 池和大型池是如何分別被重新配置為 8MB 和 16MB,并且注意為了使總的 SGA 保持在 600MB 以下,緩沖池已從 404MB 減少為 388MB。當然,這些池仍然由自動共享內存管理控制 — 它們的大小將根據需求縮小或擴展。您顯式指定的值為池的大小設定了一個下限;它們將永遠不會縮小到低于這個界限。
?
?
結論
?
??? Oracle SGA 中的各種池的內存需求不是靜態的 — 相反,它們根據系統上的需求而變化。Oracle 數據庫 10g 中的自動共享內存管理特性通過動態地將資源重新分配到最需要它們的地方同時施加一個指定的最大值以防止分頁和交換,使得 DBA 能夠更有效地管理系統內存。更有效的內存管理還帶來了更少的內存需求,這使得更精簡的硬件更加可行。
?
?
?
?
?
?
?
?