在 PreparedStatement 中设置 FetchSize 时 Oracle 驱动程序发生 OutOfMemoryError

Posted

技术标签:

【中文标题】在 PreparedStatement 中设置 FetchSize 时 Oracle 驱动程序发生 OutOfMemoryError【英文标题】:OutOfMemoryError occurred in Oracle driver when setFetchSize in PreparedStatement 【发布时间】:2014-01-16 10:34:19 【问题描述】:

我有这段代码,这段代码基本上在PreparedStatement中设置了限制,以避免加载整个表数据。

PreparedStatement pstmt = null;
    ResultSet rs = null;
    try 
        pstmt = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
        if (getDataSheet().isPagingEnabled()) 
            pstmt.setFetchSize(getDataSheet().getPageSize() + 1);
        

        if (getDataSheet().isDisableTotalRowsCount() && maxRecords >= 0) 
            if (getDataSheet().isPagingEnabled()) 
                pstmt.setMaxRows(getCurrentPageSize());
             else 
                pstmt.setMaxRows(maxRecords);
            
        

        //the rest of code.

     catch(Exception ex) 
        //handle exception
    

此代码适用于所有数据库供应商,除了 Oracle 驱动程序中的一种情况,如果我在 PreparedStatement 中使用 setFetchSize,并且页面大小超过 20000,驱动程序会抛出 OutOfMemoryError

Caused by: java.lang.OutOfMemoryError: Java heap space
at oracle.jdbc.driver.PhysicalConnection.getCharBuffer(PhysicalConnection.java:7018)
at oracle.jdbc.driver.OracleStatement.prepareAccessors(OracleStatement.java:907)
at oracle.jdbc.driver.T4CTTIdcb.receiveCommon(T4CTTIdcb.java:261)
at oracle.jdbc.driver.T4CTTIdcb.receive(T4CTTIdcb.java:127)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:992)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:194)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:791)
at oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:866)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1186)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3387)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3431)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1203)

知道为什么甲骨文会发生这种情况吗?在数据库级别有什么想法吗? 提前致谢。

【问题讨论】:

为什么要一次性获取 20k 行? fetch size 的用途是获取一批行,对其进行处理,然后再获取另一批——从而限制了到数据库的往返次数。它不是意味着在一个大规模的内存批处理中获取所有行。 【参考方案1】:

与您的想法相反,setFetchSize 不限制检索的记录数量,它确定(或提示)驱动程序应预取和缓存多少记录。 mysql 是个例外,它总是预取所有记录,除非您将提取大小设置为Integer.MIN_VALUE

驱动程序只是检索太多行,以至于内存耗尽。

【讨论】:

是的,我明白了,我对setFetchSize 有误解,好的,那么这是setFetchSize(Integer.MIN_VALUE) 的最佳做法还是最佳号码? @JUBA 不,你应该使用一个合理的数字(比如 1 到 500 之间);效果可能因驱动程序和数据库而异(我建议测试它是否真的能提高性能)。 Integer.MIN_VALUE 仅适用于 MySQL!

以上是关于在 PreparedStatement 中设置 FetchSize 时 Oracle 驱动程序发生 OutOfMemoryError的主要内容,如果未能解决你的问题,请参考以下文章

如果值为零,如何在准备好的语句中设置 null [重复]

JDBC - 如何在预准备语句中设置char

带有 IN 子句中参数列表的 PreparedStatement [重复]

在 log4j2 jdbcapender 中设置日期时如何处理 null?

如何在 java 中设置 umask?

Scikit learn 错误消息“精度和 F 分数定义不明确,在标签中设置为 0.0”[重复]