Oracle JDBC预取:如何避免RAM耗尽

使用Oracle java JDBC(ojdbc14 10.2.x),加载具有多行的查询需要永远(高延迟环境。显然,Oracle JDBC中的默认预取是默认大小“10”,这需要每10行一次往返时间。我试图设置一个积极的预取大小,以避免这种情况。

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) .... 

我还能做些什么来获取预取但不会耗尽RAM? 到底是怎么回事?

SO上最接近的相关项目是: https : //stackoverflow.com/a/14317881/32453

基本上,oracle对后来的ojdbc jar的默认策略是每个“预取”行“预分配”一个数组,该行可以容纳可能从该查询返回的最大大小。 所以在我的情况下,我有一些VARCHAR2(4000),所以50个线程* 3列varchar2的* 4000加起来超过千兆字节的RAM [yikes]。 似乎没有选项可以说“不预先分配该数组,只需使用所需的大小”。 Ojdbc甚至将这些预先分配的缓冲区保留在预备语句之间,以便它可以重用它们。 绝对是一个记忆猪。

修复是确定最大实际列大小,然后用(假设50是最大大小)替换查询select substr(column_name, 0, 50) 0,50 select substr(column_name, 0, 50)以及配置文件,并且仅使用setFetchSize的高值作为实际显着的速度改进。

您可以执行的其他操作:减少预取行数,增加Xmx参数,仅选择所需的列。

一旦我们能够使用至少prefetch 400 [确保分析以查看哪些数字对您有用,并且在所有查询中我们看到改进,直到预取大小为3-4K],性能得到显着改善。

我想如果你想对稀疏的“非常长”的行非常积极,你可能会在遇到这些[稀有]行时重新查询。

详情请点击此处