1. 程式人生 > >oracle 10g:自動共享記憶體管理

oracle 10g:自動共享記憶體管理

是不是很難準確地分配不同的池所需的記憶體數?自動共享記憶體管理特性使得自動將記憶體分配到最需要的地方去成為可能。

  無論您是一個剛入門的 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 的大小相同。

oracle <wbr>10g:自動共享記憶體管理



圖 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 改變時,如何自動重新分配池。

 
oracle <wbr>10g:自動共享記憶體管理

圖 2:在將 SGA 大小減少到 300MB 之後重新分配池

  這些池的大小是動態的。池將根據工作負載擴充套件,以容納需求的增長,或縮小以容納另一個池的擴充套件。這種擴充套件或縮小自動發生,無需 DBA 的干預,這與本文開頭的示例不同。讓我們暫時返回到那個場景,假定在初始分配後,RMAN 作業啟動,指示需要一個更大的大型池,大型池將從 4MB 擴充套件到 40MB,以容納需求。這個額外的 36MB 將從資料庫緩衝中劃出,資料庫塊緩衝將縮小,如圖 3 所示。

 
oracle <wbr>10g:自動共享記憶體管理

圖 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。

 
oracle <wbr>10g:自動共享記憶體管理

圖 4:配置非自動緩衝引數的效果

  但如果您有足夠的可用記憶體,或者上述風險可能不是那麼明顯,那應該怎麼辦?如果這樣的話,您可以通過不指定引數檔案中的引數 SGA_TARGET、通過在檔案中將其設為 0,或者通過使用 ALTER SYSTEM 動態地將其修改為 0 來關閉自動大小調整。當 SGA_TARGET 被設為 0 時,池的當前值被自動設為它們的引數。

  使用 Enterprise Manager

  您還可以使用 Enterprise Manager 10g 來處理這些引數。從資料庫主頁中單擊超連結 "Memory Parameters",這將顯示一個類似於圖 5 中的螢幕

 
oracle <wbr>10g:自動共享記憶體管理

圖 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 能夠更有效地管理系統記憶體。更有效的記憶體管理還帶來了更少的記憶體需求,這使得更精簡的硬體更加可行。

轉自:http://publish.it168.com/2006/0316/20060316030101.shtml

注意

關於statistics_level

如果想設定statistics_level=basic, sga_target必須設定為0,否則把statistics_level設定為basic後資料庫將無法啟動。

另附一OCP考題:

74. In your running instance, some of the initialization parameters are set as shown below:
SGA_MAX_SIZE = 14GB
DB_CACHE_SIZE = 1GB
SHARED_POOL_SIZE = 3GB
STATISTICS_LEVEL = BASIC
PGA_AGGREGATE_TARGET = 0
You plan to enable Automatic Shared Memory Management but you are not able to set SGA_TARGET to
a nonzero value. What could be the reason?
A.The STATISTICS_LEVEL initialization parameter is set to BASIC.
B.The PGA_AGGREGATE_TARGET initialization parameter is set to zero.
C.The SGA_MAX_SIZE initialization parameter is set to less than 20 GB.
D.The DB_CACHE_SIZE initialization parameter is set to less than 5 GB.
E.The SHARED_POOL_SIZE initialization parameter is set to a nonzero value.
Answer: A