2015-01-27 63 views
4

使用Oracle java JDBC(ojdbc14 10.2.x),加載具有多行的查詢需要永久(高延遲環境,這顯然是Oracle JDBC中的默認預取是默認大小「10」,這要求每10行的往返時間一次。我試圖建立一個積極的預取大小,以避免這一點。Oracle JDBC預取:如何避免RAM耗盡

PreparedStatement stmt = conn.prepareStatement("select * from tablename"); 
statement.setFetchSize(10000); 
ResultSet rs = statement.executeQuery(); 

這可以工作,而是我得到一個內存不足的異常。我有假設setFetchSize會告訴它在進入時緩衝「那麼多行」,並使用每行所需的RAM數量。如果我使用50個線程運行,即使是16G的-XMX空間,它也會耗盡內存。像泄漏:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
    at java.lang.reflect.Array.newArray(Native Method) 
    at java.lang.reflect.Array.newInstance(Array.java:70) 
    at oracle.jdbc.driver.BufferCache.get(BufferCache.java:226) 
    at oracle.jdbc.driver.PhysicalConnection.getCharBuffer(PhysicalConnection.java:7422) 
    at oracle.jdbc.driver.OracleStatement.prepareAccessors(OracleStatement.java:983) 
    at oracle.jdbc.driver.T4CTTIdcb.receiveCommon(T4CTTIdcb.java:273) 
    at oracle.jdbc.driver.T4CTTIdcb.receive(T4CTTIdcb.java:144) 
    at oracle.jdbc.driver.T4C8Oall.readDCB(T4C8Oall.java:771) 
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:346) 
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186) 
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:521) 
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:205) 
    at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:861) 
    at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1145) 
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1267) 
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3449) 
    at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3493) 
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1491) 
    .... 

我能做些什麼仍然可以預取但不會耗盡內存?到底是怎麼回事?

上SO與最接近相關的項目是這樣的:https://stackoverflow.com/a/14317881/32453

回答

5

基本上,後來ojdbc罐子Oracle的默認策略是每個「預取」一行可容納的最大尺寸可以想到,返回到「預分配」的數組來自該查詢。所以在我的情況下,我有一些VARCHAR2(4000)在那裏,所以50個線程* 3列varchar2的* 4000加起來超過千兆字節的RAM [yikes]。似乎沒有選擇說「不要預先分配該數組,只需使用所需的大小」。 Ojdbc甚至將這些預分配緩衝區保留在之間的準備狀態之間,以便它可以重用它們。絕對是一個記憶豬。

解決的辦法是確定最大實際列大小,然後替換查詢(假設50是最大大小)select substr(column_name, 0, 50)以及配置文件,並且只使用setFetchSize的最高值作爲實際顯着的速度改進。

你可以做的其他事情:減少預取行數,增加Xmx參數,只選擇你需要的列。一旦我們能夠使用至少預取400 [確保配置文件查看哪些數字對您有好處,並且具有高延遲,我們看到所有查詢的預取大小都提高了3-4K],則性能顯着提高。

我想如果你想對稀疏的「非常長」的行進行真正的攻擊,你可能能夠在你遇到這些[稀有]行時重新查詢。

詳情ad nauseum here