Oracle JDBC驅動內存

1、JDBC驅動內存機制

       Oracle爲了提升數據庫訪問性能,提供了服務器端緩存和客戶端緩存這兩種緩存。其中,Oracle客戶端緩存可能會使用大量的內存,這是一種有意識的設計選擇,在使用大量內存與提升性能之間作出權衡,用內存換取性能。sql

     其中主要對內存產生較大影響的緩存有如下幾種:數據庫

     1.查詢結果緩存緩存

     JDBC驅動緩存中緩存的是sql的執行結果,這樣相同sql再次執行時能夠直接從緩存中獲取數據,而不須要與服務器端交互,從而達到提升查詢性能的做用。默認狀況下,咱們在使用一個數據庫鏈接去執行查詢時,JDBC驅動會爲鏈接內的每個Statement(Statement、PreparedStatement、CallableStatement)建立兩個buffer:byte[]和char[]。其中char[]用來保存全部字符類型的行數據,如:CHAR,VARCHAR2,NCHAR等,byte[]用來保存全部的其它類型的行數據。這些buffer在在SQL被解析的時候分配,通常也就是在第一次執行該Statement的時候。Statement會持有這兩個buffer,直到它被關閉。服務器

     2.PreparedStatement緩存oracle

     大型系統中每每不少次執行相同的sql,出於性能方面考慮,驅動重用了PreparedStatement,而不是每次爲每條sql建立新的PreparedStatement。JDBC驅動內置了一個語句緩存-- Implicit Statement Cache(它的大小默認爲0,即不緩存PreparedStatement)。這個語句緩存對用戶是透明的,可經過配置鏈接屬性,設置緩存的PreparedStatement數量。性能

     3.buffer緩存fetch

     Oracle11.2版本驅動中,驅動程序中提供了一個內存管理更復雜的方法。這個方法有兩個目標,最大限度減小內存未使用量和最小化buffer分配帶來的成本。驅動程序在每一個鏈接內部建立了一個buffer緩存(buffer cache)。當一個PreparedStatment使用結束被放回Implicit Statement Cache中時,它的buffer會被緩存到buffer緩存中buffer桶(buncket)中,一個buffer桶中的全部buffer都是相同大小且這個大小是預先肯定的。當一個PreparedStatement是從Implicit Statement中取出時,也同時會從buffer緩存中根據查詢結果的大小從適當的buffer桶中取出buffer。優化

驅動提供一個鏈接屬性oracle.jdbc.maxCachedBufferSize。它是一個int字符串,默認是Integer.MAX_VALUE。此屬性限定了保存在buffer桶中buffer最大的長度大小。超過這個大小 的buffer會在PreparedStatement被放回Implicit Statement Cache中時釋放,小於這個大小的buffer會被緩存到相應的buffer桶中,當PreparedStatement從Implicit Statement Cache中取出時,小於maxCachedBufferSize的buffer會從合適的buffer 桶中取出,大於maxCachedBufferSize的buffer 會被從新建立。spa

    

2、內存佔用狀況

       buffer是在SQL解析的時候被分配的,buffer的大小並不取決於查詢返回的行數據的實際長度,而是行數據可能的最大的長度。在SQL解析時,每列的類型是已知的,從該信息中驅動程序能夠計算存儲每一列所需的內存的最大長度。驅動程序也有fetchSize屬性,也就是每次fetch返回的行數。有了每列有大小和行數的大小,驅動程序能夠由此計算出一次fetch所返回的數據最大絕對長度。這也就是所分配的buffer的大小。hibernate

     字符數據存儲在char[]buffer中。Java中的每一個字符佔用兩個字節。一個VARCHAR2(10)列將包含最多10個字符,也就是10個Java的字符,也就是每行20個字節。一個VARCHAR2(4000)列將佔用每行8K字節。重要的實際上是column的定義大小,而不是實際數據的大小。一個VARCHAR2(4000)可是隻包含了NULL的列,仍然須要每行8K字節。buffer是在驅動程序看到的查詢結果以前被分配的,所以驅動程序必須分配足夠的內存,以應付最大可能的行大小。一個定義爲VARCHAR2(4000)的列最多可包含4000個字符。Buffer必須大到足以容納4000個字符分配,儘管實際的結果數據可能沒有那麼大。

     BFILE,BLOB和CLOB會被存儲爲locator。Locator可高達4K字節,每一個BFILE,BLOB和CLOB列的byte[]必須有至少每行4K字節。RAW列最多能夠包含4K字節。其它類型的則須要不多的字節。一個合理的近似值是假設全部其它類型的列,每行佔用22個字節。

      假設目前數據庫中,每條記錄定義:10K,fetchsize:50,PreparedStatementCache:100,鏈接池max值:30, 鏈接池個數:2個。在極端狀況下,內存佔用會達到:2*10K*50*100*30*2=6GB 。

3、控制參數說明

       說明如下參數均可以經過-D方式添加到啓動sofa容器的啓動參數中設置系統屬性。

 

     1.oracle.jdbc.maxCachedBufferSize

此屬性限定了保存在buffer緩存中buffer的最大的長度大小。它是一個int字符串,默認是Integer.MAX_VALUE。超過這個大小 的buffer會在PreparedStatement被放回Implicit Statement Cache中時釋放,小於這個大小的buffer會被緩存到相應的buffer桶中,當PreparedStatement從Implicit Statement Cache中取出時,小於maxCachedBufferSize的buffer會從合適的buffer 桶中取出,大於maxCachedBufferSize的buffer 會被從新建立。

     2. oracle.jdbc.useThreadLocalBufferCache

默認值:false,默認狀況下,buffer緩存中存儲在鏈接中的,即每一個鏈接都會有本身的緩存。若是在真實業務場景中,咱們存在大量這樣的使用場景:在一個處理邏輯中,咱們須要使用多個鏈接去訪問數據庫,那麼這些鏈接各自都有本身的緩存。且相對於線程數來講存在大量空閒數據庫鏈接,因爲默認狀況下buffer緩存是附屬於每一個鏈接的,空閒鏈接的結果就是buffer緩存中有不少不會被用到的buffer,會用不少不必的內存。咱們能夠把oracle.jdbc.useThreadLocalBufferCache屬性設置爲true,這樣咱們可讓業務處理邏輯所在線程內的全部鏈接共用一個緩存,即把buffer存儲到線程中,如此一來,線程內使用到的鏈接都會共用當前線程內的buffer。該屬性能夠經過-D設置System property或者經過的調用getConnection時的connection property。

    3. OracleStatement.setLobPrefetchSize

              LOB字段默認大小爲4000bytes。設置BLOB字段每次獲取字節數據的大小,這種方式也能夠限制一行查詢結果的大小。因sofa底層使用的是hibernate,不支持對該參數的設置控制。在使用jdbc方式時能夠設置該參數。

     4.fetchSize

              buffer的大小不是由查詢的實際行數決定的,而是由每次獲取的行數決定的。這個每次獲取的行數也就是fetchSize,見下圖。

      

JDBC驅動默認是10。咱們能夠在hibernate_properties_config.xml中經過配置

   <prop key="hibernate.jdbc.fetch_size">50</prop>來設置。

       或在鏈接池中按不一樣鏈接池的屬性去配置fetchSize大小。

5.優化鏈接池

        buffer默認是存儲在每一個數據庫鏈接中的。鏈接池中配置太多的鏈接,會致使鏈接大部分空閒。在極端狀況下,若是鏈接池內的鏈接被所有使用,每一個鏈接內都會有查詢結果緩存,就會致使應用程序內存佔用暴增。

相關文章
相關標籤/搜索