【问题标题】:How to improve performance of retrieving a REF CURSOR into Java using Spring?如何提高使用 Spring 将 REF CURSOR 检索到 Java 中的性能?
【发布时间】:2012-09-13 02:03:21
【问题描述】:

我正在调用作为数据库包一部分的函数。该软件包部署在两个位置。一个本地和另一个远程(横跨大西洋)。

我正在通过 Spring JDBC 模板检索数据。

有一个函数返回大约 1000 行(不是那么多),在本地获取数据时大约需要 1.5 秒,但在远程获取数据时大约需要 12 秒。

在所有示例代码中,名称都已更改,代码也有所简化。

请看一个当前Java代码的例子:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(getDataSource())
                                            .withSchemaName(MY_SCHEMA_NAME)
                                            .withCatalogName("REFCURSOR_PKG")
                                            .withFunctionName("GET_DATA")
                                            .returningResultSet("RESULT_SET", new DataEntryMapper());

        SqlParameterSource params = new MapSqlParameterSource()
                                    .addValue("the_name", name)
                                    .addValue("the_rev", rev);

        Map resultSet = simpleJdbcCall.execute(params);
        ArrayList list = (ArrayList) resultSet.get("RESULT_SET");

RowMapper 类看起来像这样:

class RouteDataEntryMapper implements RowMapper {
        public RouteDataEntry mapRow(ResultSet resultSet, int rowNum) throws SQLException {
            return new DataEntry(resultSet.getString("name"), 
                                    Integer.parseInt(resultSet.getString("rev"));
        }
    }

SQL 包规范 sn-p:

TYPE REF_CURSOR IS REF CURSOR;  

SQL函数:

 FUNCTION GET_ROUTE_DATA(the_name VARCHAR2, the_rev VARCHAR2) RETURN REF_CURSOR AS

  RESULT_SET REF_CURSOR;

  BEGIN
    OPEN RESULT_SET FOR
    select *
    from table_name tn
    where tn.name = the_name
    and tn.rev = the_rev;

    RETURN RESULT_SET;

    CLOSE RESULT_SET;

    EXCEPTION WHEN OTHERS THEN
      RAISE;

  END GET_ROUTE_DATA;

我也尝试过使用常规样板 JDBC(创建连接、准备语句、执行语句、从 RESULT_SET 检索数据等),我发现绝大多数时间都花在循环 RESULT_SET 并从中提取数据它和一些pojos。在上面的 Spring 代码的情况下,大部分时间都花在了 execute() 方法中,但这可能是因为它当时使用 RowMapper 创建了对象。

因此,它们之间的共同领域是执行以下操作:

rs.getString("name")

我猜这就是问题所在,但我可能错了。

正如我所说,本地延迟很好,但远程延迟太长了。这是因为它会在每个 rs.get... 上进入数据库吗?有没有更好的方法来做到这一点?

提前致谢。

【问题讨论】:

  • 您的Statement 和/或ResultSet 的获取大小是多少?您是否尝试过增加提取大小?
  • 您是否能够比较速度与在没有 PL/SQL 包装器的情况下运行相同的 SQL 查询?是 REF_CURSOR 带来了额外的缓慢,还是实际上检索的查询结果很慢?另外,响应集中的列类型是什么;如果有任何 LONG/*LOB 列,它们将导致单独的服务器往返(即使您使用 rs.getString() 获取 CLOB)。
  • 获取大小是问题所在。我添加了一些日志语句,发现每 10 个 resultSet.next() (使用样板 JDBC 方法)都需要时间(跨站点时大约 140 毫秒)。我将获取大小更改为 1000,这使它更快(第一个 resultSet.next() 大约需要 550-600 毫秒,但仅此而已,整体操作需要 1.5 秒,而之前需要 12-14 秒)。但是,我现在在尝试弄清楚如何使用 Spring SimpleJdbcCalll 设置提取大小时遇到​​了麻烦,但至少现在我知道我需要做什么。谢谢@JustinCave!
  • 找到了在 Spring 中设置 fetch size 的解决方案。只需在 RowMapper 类的 mapRow 方法中设置 ResultSet 对象的 fetch size 即可。在这里找到解决方案:stackoverflow.com/a/2407573/487812

标签: java sql oracle spring


【解决方案1】:

rs.getString("name")

ResultSet.get*(String columnName) 可以替换为 ResultSet.get*(int columnNaumber) 稍微快一点,但我怀疑这里的主要问题。

这是因为它会在每个 rs.get... 上进入数据库吗?

虽然这确实取决于驱动程序,但我怀疑它不会。对于缓存的结果集,它可能会在您滚动游标时转到服务器,但它仍会在每次往返中获取一堆行。

我还有两个建议:

  • 使用网络嗅探实用程序查看正在传输的数据
  • 检查您的驱动程序是否有任何预取选项等。

【讨论】:

  • 感谢您的评论。它也帮助了我。请参阅上面对我的问题的评论。
【解决方案2】:

添加这一行:-

.withoutProcedureColumnMetaDataAccess

在以下代码行中

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(getDataSource())
                                            .withSchemaName(MY_SCHEMA_NAME)
                                            .withCatalogName("REFCURSOR_PKG")
                                            .withFunctionName("GET_DATA")
                                            .withoutProcedureColumnMetaDataAccess  // to avoid fetching meta data info from database

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-13
    • 2018-12-10
    • 2012-10-28
    • 1970-01-01
    • 2017-05-14
    相关资源
    最近更新 更多