【问题标题】:Why is retrieving a ResultSet from Oracle stored procedure so slow?为什么从 Oracle 存储过程中检索 ResultSet 这么慢?
【发布时间】:2011-02-24 17:15:04
【问题描述】:

我必须改进一些从 Java 程序调用 Oracle 存储过程的代码。目前代码真的很慢:在我的开发机器上最多大约 8 秒。在同一台机器上,如果我直接调用一个执行相同处理并返回相同数据的 SQL 查询,则需要不到 100 毫秒...

代码创建一个 CallableStatement,将输出参数之一注册为 Oracle 游标,然后使用语句的 getObject 方法检索游标并将其解析为 ResultSet:

cstmt = conn.prepareCall("{ call PKG_ESPECEW.P_ListEspece( ?, ?, ?, ?, ?, ? ) }");
cstmt.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR);
[...]
cstmt.executeQuery();
rs = (ResultSet)cstmt.getObject(4);
rs.setFetchSize(1000); //supposed to help ?

options = new HashMap<String, String>(1000);
rs.next() //added that to measure exactly the length of the first call

while(rs.next()) {
    [...]
}

我在代码中添加了一些时间戳,以了解哪个部分花费了这么长时间。结果:对rs.next() 的第一次调用最多需要几秒钟。结果集是平均的,从 10 到几千行。正如我之前所说,处理来自常规 PreparedStatement 的类似结果集需要 10-100 毫秒,具体取决于大小。

代码有什么问题吗?我该如何改进它?如果我没有任何其他解决方案,我会在关键的地方直接执行 SQL,但我更喜欢允许我不重写所有过程的解决方案!

这里是存储过程的定义:

PROCEDURE P_ListEspece(P_CLT_ID IN ESPECE.ESP_CLT_ID%TYPE,     -- Langue de l'utilisateur
                        P_ESP_GROUP_CODE IN ESPECE.ESP_CODE%TYPE,-- Code du groupe ou NULL
                        P_Filter IN VARCHAR2,                   -- Filtre de la requête
                        P_Cursor OUT L_CURSOR_TYPE,             -- Curseur
                        P_RecordCount OUT NUMBER,               -- Nombre d'enregistrement retourne
                        P_ReturnStatus OUT NUMBER);              -- Code d'erreur

【问题讨论】:

  • 有什么理由不使用OracleCallableStatement.getCursor 而不是getObject
  • 我必须将 CallableStatement 转换为 OracleCallableStatement,但我使用的是 DBCP,而 CallableStatement 实际上是 DBCP 提供的“代理”,所以我会得到一个异常(我试过了)。跨度>
  • 对于获取,要通过网络优化它,您应该使用 setRowPrefetch。这具有巨大的影响。使用 setFetchSize 大约为 20-30% 时,Prefetch 在大型数据集上可以达到 10 倍。

标签: java oracle jdbc


【解决方案1】:

"我以为程序被执行了,然后结果存储在oracle服务器的内存中,最后通过游标和结果集和JDBC传回客户端(java app)"

这是不正确的。 oracle 作为游标返回的基本上是一个指向查询的指针(所有绑定变量都准备好了)。它还没有具体化内存中的结果集。它可能是包含数百万/数十亿行的庞大结果集。

因此,它很可能是一个缓慢的查询,需要很长时间才能提供结果。

【讨论】:

  • 我与负责数据库的数据库人员进行了交谈,似乎在 SP 内执行的查询实际上是问题所在,因为它正在调用一个函数,该函数为找到的每条记录打开另一个游标。他删除了这个调用,执行时间降到了不到 1 秒。它仍然很慢,但已经更容易接受了。我猜查询本身仍然可以改进,问题实际上是查询而不是数据的传输。
【解决方案2】:

显然存储过程正在进行一些数据转换/来回传递(例如int varchar)。众所周知,在大型表的情况下这会花费大量时间。确保您在 SP 参数中声明了正确的数据类型,并在 CallableStatement 中设置了正确的数据类型。

【讨论】:

  • 实际上不是读取或设置 SP 参数需要时间,而是从本身就是 SP 的参数的 ResultSet 中读取。我将使用 SP 定义更新问题。所以你认为在这种情况下类型转换可能是一个问题?
  • 您告诉过PreparedStatement 更快,因此问题可能出在 SP 上。如果需要(隐式)转换任何列值,则需要时间,尤其是在 SP 的 WHERE 子句中使用时。
  • 但它不是 cstmt.executeQuery();那很慢,这是结果集浏览。我以为程序被执行了,然后它的结果存储在oracle服务器的内存中,最后通过游标和结果集和JDBC传输回客户端(java应用程序)。但也许我错了?我是一个 Oracle/PLSQL 无知。读取结果集时是否会“逐步”执行该过程?
  • 我检查了所有参数的映射:除了第二个之外,它们都是正确的。它是 NUMBER 类型,但我在语句上应用了 setInt 方法。我更正了这个,但不幸的是它并没有解决响应时间慢的问题……我认为这个参数的转换只会由 Oracle 完成一次,所以影响不大……
【解决方案3】:

在 Java 之外执行该过程需要多长时间?在 SQL*Plus 中检查这样的脚本:

var ref refcursor
var cnt number
var status number
exec p_listespece (xx, yy, zz, :ref, :cnt, :status);--replace with actual values
print :ref

如果超过 10-100 毫秒,你的问题可能来自存储过程。

【讨论】:

  • 实际上我的开发机器上没有安装 SQLPlus 或 oracle 客户端。我使用 SQLDeveloper 完成大部分工作,其余的则向 DBA 询问。明天我会和他核实一下。或者你知道我是否可以直接从 SQLDev 执行 plsql ?
  • 我安装了oracle客户端和sql plus并尝​​试了这个。在命令行中显示结果需要很长时间,但它可能会因 windows 命令行中的命令行打印而减慢。无论如何,问题出在 SP 本身。
【解决方案4】:

我遇到了同样的问题,我们通过将返回的参数从游标更改为 varchar 解决了(我和 oracle 专门的人),这是存储在内部执行的普通查询。 这是一个巨大的实现,我不知道这是否适用于您的场景。

这是sn-p:

`

String sql = "call MyStored(?,?,?,?)";
CallableStatement st = Conn.prepareCall(sql);
st.setInt(1, 10);
st.setInt(2, 20);
st.setInt(3, 30);
st.registerOutParameter(4, OracleTypes.VARCHAR);

st.execute();

String query = (String) st.getObject(4);
Statement stmt = Conn.createStatement();
rs = stmt.executeQuery(query);
[...]
//work with resultset
[...]
stmt.close();
stmt = null;

`

【讨论】:

    猜你喜欢
    • 2010-10-05
    • 2013-09-19
    • 2020-03-19
    • 1970-01-01
    • 2011-12-19
    • 1970-01-01
    • 1970-01-01
    • 2011-03-21
    • 2013-06-04
    相关资源
    最近更新 更多