Oracle JDBC 预取:如何避免 运行 出 RAM/how 以使 oracle 更快高延迟
Oracle JDBC prefetch: how to avoid running out of RAM/how to make oracle faster high latency
使用 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 会告诉它在它们进入时缓冲 "that many rows",使用每行所需的尽可能多的 RAM。如果我 运行 有 50 个线程,即使有 16G 的 -XMX space,它也会 运行 内存不足。感觉几乎像泄漏:
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 上最接近的相关项目是:
基本上,oracle 对最近的 ojdbc jar 的默认策略是为每个“预取”行“预分配”一个数组,以适应该查询中可能 return 的最大尺寸。对于所有行。所以在我的例子中,我有一些 VARCHAR2(4000),50 个线程(语句)* varchar2 的 3 列 * 4000 加起来超过千兆字节的 RAM,setFetchSize 为几百 [yikes]。似乎没有一个选项可以说“不要预先分配该数组,只需使用它们进来时的大小”。 Ojdbc 甚至将这些预先分配的缓冲区保留在 preparedstatements (cached/connection) 之间,以便它可以重用它们。绝对是个内存大户。
一个解决方法:使用 setFetchSize
到一些合理的数量。默认值为 10,这在高延迟连接上可能会非常慢。配置文件并仅使用尽可能高的 setFetchSize 实际上可以显着提高速度。
另一种解决方法是确定最大实际列大小,然后将查询替换为(假设 50 是已知的最大实际大小)select substr(column_name, 0, 50)
您可以做的其他事情:减少预取行数,增加java -Xmx
参数,只select您实际需要的列。
一旦我们能够在所有查询上至少使用预取 400 [确保进行分析以查看哪些数字对您有好处,在高延迟的情况下,我们看到了预取大小 3-4K 的改进],性能显着提高.
我想如果你想对稀疏的“非常长”的行采取真正积极的态度,你可以在 运行 进入这些 [稀有] 大行时重新查询。
细节令人作呕 here
使用 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 会告诉它在它们进入时缓冲 "that many rows",使用每行所需的尽可能多的 RAM。如果我 运行 有 50 个线程,即使有 16G 的 -XMX space,它也会 运行 内存不足。感觉几乎像泄漏:
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 上最接近的相关项目是:
基本上,oracle 对最近的 ojdbc jar 的默认策略是为每个“预取”行“预分配”一个数组,以适应该查询中可能 return 的最大尺寸。对于所有行。所以在我的例子中,我有一些 VARCHAR2(4000),50 个线程(语句)* varchar2 的 3 列 * 4000 加起来超过千兆字节的 RAM,setFetchSize 为几百 [yikes]。似乎没有一个选项可以说“不要预先分配该数组,只需使用它们进来时的大小”。 Ojdbc 甚至将这些预先分配的缓冲区保留在 preparedstatements (cached/connection) 之间,以便它可以重用它们。绝对是个内存大户。
一个解决方法:使用 setFetchSize
到一些合理的数量。默认值为 10,这在高延迟连接上可能会非常慢。配置文件并仅使用尽可能高的 setFetchSize 实际上可以显着提高速度。
另一种解决方法是确定最大实际列大小,然后将查询替换为(假设 50 是已知的最大实际大小)select substr(column_name, 0, 50)
您可以做的其他事情:减少预取行数,增加java -Xmx
参数,只select您实际需要的列。
一旦我们能够在所有查询上至少使用预取 400 [确保进行分析以查看哪些数字对您有好处,在高延迟的情况下,我们看到了预取大小 3-4K 的改进],性能显着提高.
我想如果你想对稀疏的“非常长”的行采取真正积极的态度,你可以在 运行 进入这些 [稀有] 大行时重新查询。
细节令人作呕 here