Oracle JDBC 预取:如何避免 RAM 不足/如何使 oracle 更快高延迟

2024-05-18

使用 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 https://stackoverflow.com/a/14317881/32453


基本上,Oracle 对于最近的 ojdbc jar 的默认策略是为每个“预取”行“预分配”一个数组,以适应从该查询返回的可能的最大大小。对于所有行。因此,在我的例子中,我有一些 VARCHAR2(4000) 在那里,50 个线程(语句)* 3 列 varchar2 * 4000 加起来超过 GB 的 RAM,setFetchSize 为几百[哎呀]。似乎没有一个选项可以说“不要预先分配该数组,只需使用它们进来的大小”。 Ojdbc 甚至保留这些预分配的缓冲区准备好的语句之间(缓存/连接)以便可以重用它们。绝对是内存大户。

一种解决方法:使用setFetchSize达到一定的合理数量。默认值为 10,在高延迟连接上可能会非常慢。配置文件并仅使用与实际一样高的 setFetchSize,从而显着提高速度。

另一种解决方法是确定最大实际列大小,然后将查询替换为(假设 50 是已知的最大实际大小)select substr(column_name, 0, 50)

您可以做的其他事情:减少预取行数,增加 java-Xmx参数,只选择您实际需要的列。

一旦我们能够在所有查询上使用至少预取 400 [确保分析以了解哪些数字对您有利,由于高延迟,我们看到预取大小提高到 3-4K],性能就会显着提高。

我想,如果您想真正积极地应对稀疏的“非常长”的行,那么当您遇到这些[罕见的]大行时,您可能可以重新查询。

细节令人作呕here http://www.oracle.com/technetwork/database/enterprise-edition/memory.pdf

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Oracle JDBC 预取:如何避免 RAM 不足/如何使 oracle 更快高延迟 的相关文章

随机推荐