JVM虛擬機和Oracle數據庫內存管理的學習

JVM虛擬機和Oracle數據庫內存管理的學習


背景

週四陪孩子喫早飯時看到了DB寶推送的 Oracle shared_pool內存的文章.
然後一路看到公司, 發現裏面寫的跟最近自己在看的jvm內存有很多相通的地方.

所以想嘗試總結一下兩者內存管理的一些可以用來提高性能的地方

Oracle的內存分佈

Oracle的管理,其實與操作系統也非常相似.
主要也是分爲: 內存管理, 線程管理, 存儲管理. 
存儲管理就是 物理文件存儲和邏輯存儲.  (其實還有ASM)
線程管理 主要分爲 用戶的線程和數據庫本身的線程.

內存管理就是今天想學習的核心
分爲PGA和SGA
PGA 線程私有 SGA 是線程共享區域

其實JVM的內存管理, 也是分爲 堆區和非堆區
堆區都是線程共享的.
非堆區裏面的 線程棧區域是線程私有的.
兩者很相似. 

關於線程共享內存的查看

SELECT
	pool,
	round( sum( bytes / 1024 / 1024 ), 0 ) bytes 
FROM
	v$sgastat 
GROUP BY
	pool 
ORDER BY
	bytes DESC;

我這邊一個測試環境爲:

池類型 池大小(單位MB)
未命名的池 280064
shared pool 8971
java pool 3584
streams pool 1003
large pool 717

OracleSGA區域JVM區域的對照

最大的一部分: 未命名的內存池
包含:
塊緩衝區(緩存的數據庫塊)、
重做日誌緩衝區
“固定SGA”區專用的內存

fixed area 應該都有, 是一個自啓,並且進行鏈接其他區域使用.
Redo buffer 就是重做日誌的區域, 這個區域其實不能太大,
 事務提交,必須要落盤的. 寫入buffer 並不安全.

block buffer 這裏面是解決物理讀寫, 改成邏輯讀寫的核心部分. 
他又分爲: 
默認池(default pool):所有段塊一般都在這個池中緩存。這就是原先的緩衝區池
    (原來也只有一個緩衝區池)。
保持池(keep pool):按慣例,訪問相當頻繁的段會放在這個候選的緩衝區池中,
    如果把這些段放在默認緩衝區池中,儘管會頻繁訪問,
    但仍有可能因爲其他段需要空間而老化(aging)。
回收池(recycle pool):按慣例,訪問很隨機的大段可以放在這個候選的緩衝區池中,
    這些塊會導致過量的緩衝區刷新輸出,而且不會帶來任何好處,因爲等你想要再用這個塊時,
    它可能已經老化退出了緩存。要把這些段與默認池和保持池中的段分開,
    這樣就不會導致默認池和保持池中的塊老化而退出緩存。

其實 未命名的這一塊就很想jvm 裏面的堆區
fixed 類似於 類加載器加載的那一塊區域. 是bootstrap用的部分. 
redo buffer 其實應該對比 直接內存, 但是jvm 一般沒有寫數據庫的動作,可以暫時不這樣對比.
block buffer 就是跟最大的堆區對照了. 

他的保持區 就類似於老年代. 
默認池的話就類似於 咱們的青年代 
回收池 其實jvm沒有這個設置, 他更類似於 大對象直接進入老年代的概念了. 

OracleSGA區域JVM區域的對照

shared pool(共享池)
共享池裏面 會緩存 SQL 的代碼執行計劃等內容.
類似於 JVM裏面的code cache.

這個區域太小了 肯定存在問題, 會導致執行計劃的換入換出, 
捯飭重新進行SQL解析和執行計劃的綁定. 
但是太大了也不好, 會導致遍歷執行計劃的效率降低. 速度會變慢. 

JVM的code cache也一樣. 太小了, 會導致C2編譯進程效率降低
如果flush了 會重新編譯, 導致浪費應用的CPU
如果Code cache太大了, 可能也會影響獲取編譯後方法的效率.
以及產生大量的內存碎片進而影響性能. 

OracleSGA區域JVM區域的對照

large pool 大池

    大池(large pool)並不是因爲它是一個“大”結構才這樣取名(不過,它可能確實很大)。
之所以稱之爲大池,是因爲它用於大塊內存的分配,共享池不會處理這麼大的內存塊。
    在Oracle 8.0引入大池之前,所有內存分配都在共享池中進行。如果你使用的特性要利用“大塊
的”內存分配(如共享服務器UGA內存分配),倘若都在共享池中分配就 不太好。
另外,與共享池管理內存的方式相比,處理(需要大量內存分配)會以不同的方式使用內存,
所以這個問題變得更加複雜。共享池根據LRU來管理內存, 這對於緩存和重用數據很合適。
不過,大塊內存分配則是得到一塊內存後加以使用,然後就到此爲止,沒有必要緩存這個內存。
我的理解是:其實是把原來屬於共享 池裏面的一些特殊的內存拿出來進行不同的處理。
因爲這些內存用完之後就可以立即釋放,而共享池的內存不存在釋放問題,因爲是大家共享的。

     大池專門用於以下情況:
共享服務器連接,用於在SGA中分配UGA區,因爲一個用戶斷開之後,UGA就可以立即釋放!
語句的並行執行,允許分配進程間的消息緩衝區,這些緩衝區用於協調並行查詢服務器。
    一旦發送了緩衝消息就可以立即釋放!
備份,在某些情況下用於RMAN磁盤I/O 緩衝區。因爲寫入磁盤之後,這些緩存可以立即釋放!

Java pool Java池

    在數據庫中運行Java代碼時用到這部分內存。例如:編寫Java存儲過程在服務器內運行。
需要注意的是,該內存與常見的Java編寫的B/S系統並沒關係。
用JAVA語言代替PL/SQL語言在數據庫中寫存儲過程纔會用到這部分內存。

Stream pool 流池

    9iR2以上增加了“流”技術,10g以上在SGA中增加了流池。流是用來共享和複製數據的工具。

關於內存的理解

1. jvm的堆區和Oracle的block buffer 有着相同的作用. 
加速系統的使用效率. 
如果數據庫的block buffer 太小, 會導致數據塊被換出,可能產生大量的物理讀.導致性能衰退
JVM的堆區如果太小, 會導致經常fullGC,導致卡頓和性能衰退. 

大頁內存以及快速內存都可能對性能有正向的作用. 

2. 關於快速CPU的影響.
數據庫和JVM都應該使用高主頻的CPU. 高主頻的CPU 會減少栓鎖,自旋的時間. 
高主頻的CPU 能夠快速的完成鎖的佔用和使用, 能夠在相同時間內完成更多的事務
提高吞吐量. 

一個簡單的猜想, 理論上主頻的提升相對於性能應該是平方倍數, 或者是更高的效率提升. 

3. 關於Oracle的shared pool 和 jvm的方法區和類壓縮區

shared pool 會存儲 經過語法語義解析以及CBO 產生執行計劃的SQL. 
SQL通過hash值進行存放, 還有一個問題是會產生多版本. 
內存太多了會碎片化, 內存太小了會導致換入換出.
還有很嚴重的問題是 shared_pool 其實是基於統計信息進行生產執行計劃
按理說進行了統計分析,需要重新進行執行計劃的創建. 
如果表變化比較快 或者是更改過索引, 重新收集過統計信息
可以需要重新進行一下設置. 
此時. 綁定變量的SQL和不綁定變量的SQL執行時間可能是不一樣的
必須通過執行計劃 進行分析. 

跟jvm的方法區類似.
不能太小, 如果不刷新方法區,會導致產品變慢, 代碼無法進行深度優化. 
也不建議太大. 可能會導致方法區出現碎片化. 

此時合理的進行方法區的清理機制就很重要了. 
JVM 會根據執行次數 進行不同深度的優化. 

執行1000次和執行一千萬次需要的優化層級是不一樣的. 
而且jvm內部還有一些內聯, 會將代碼組合, 加快性能.

此時這裏 不同架構需要的方法區的大小就不一樣了. 
理論上 CISC的native code代碼可能會比 RISC的要小一些.
建議不同架構不同操作系統 上面還是進行定製化處理好一些. 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章