java - 绑定(bind)参数慢,使用 JdbcTemplate 更慢

标签 java spring oracle jdbc jdbctemplate

我的 Oracle 11g 数据库中有一个四列表,实现了扩展表反模式。我注意到一些查询花费了很长时间,并努力创建更好的索引;在交互式 session 中很好,但使用 Spring 的 NamedJdbcTemplate 仍然很慢.

考虑以下例程:

private void getObjectIds(ObjectDomain domain, HashMap<String, List<String>> dimensionMap)
    throws SQLException {
String sql = "SELECT m2.OBJECT_ID"
    + "  FROM MetaInformations m1, MetaInformations m2\n"
    + "  WHERE m1.OBJECT_ID = m2.OBJECT_ID\n"
    + "    AND m1.OBJECT_DOMAIN = :domain AND m1.KEY = :key1 AND\n"
    + "        m1.REF_OBJ_VALUE IN (:values1)\n"
    + "    AND m2.OBJECT_DOMAIN = :domain AND m2.KEY = :key2 AND\n"
    + "        m2.REF_OBJ_VALUE IN (:values2)";
String sqlWithBind = "SELECT m2.OBJECT_ID\n"
    + "  FROM MetaInformations m1, MetaInformations m2\n"
    + "  WHERE m1.OBJECT_ID = m2.OBJECT_ID\n"
    + "    AND m1.OBJECT_DOMAIN = ? AND m1.KEY = ? AND\n"
    + "        m1.REF_OBJ_VALUE IN (?, ?, ?, ?)\n"
    + "    AND m2.OBJECT_DOMAIN = ? AND m2.KEY = ? AND\n"
    + "        m2.REF_OBJ_VALUE IN (?)";

// Prebuilding statement, no bind variables left
Stopwatch stopWatch2 = Stopwatch.createStarted();
Iterator<Entry<String, List<String>>> entries = dimensionMap.entrySet().iterator();
Entry<String, List<String>> entry1 = entries.next();
Entry<String, List<String>> entry2 = entries.next();
String prebuilt = sql.replace(":domain", "'" + domain + "'")
    .replace(":key1", "'" + entry1.getKey() + "'")
    .replace(":values1",
        entry1.getValue().stream().map(s -> "'" + s + "'").collect(Collectors.joining(", ")))
    .replace(":key2", "'" + entry2.getKey() + "'")
    .replace(":values2",
        entry2.getValue().stream().map(s -> "'" + s + "'").collect(Collectors.joining(", ")));
Set<Long> rs2 = extractIdSet(getNamedParameterJdbcTemplate().queryForRowSet(prebuilt, Collections.emptyMap()));
log.warn("Prebuilt took: {} ms", stopWatch2.elapsed(TimeUnit.MILLISECONDS));

// Simple JDBCTemplate with 9 bind parameters
Stopwatch stopWatch5 = Stopwatch.createStarted();
Set<Long> rs1 = extractIdSet(getJdbcTemplate().queryForRowSet(sqlWithBind,
    domain.toString(),
    entry1.getKey(),
    entry1.getValue().get(0),
    entry1.getValue().get(1),
    entry1.getValue().get(2),
    entry1.getValue().get(3),
    domain.toString(),
    entry2.getKey(),
    entry2.getValue().get(0)));
log.warn("JdbcTemplate took: {} ms", stopWatch5.elapsed(TimeUnit.MILLISECONDS));

// Most beautiful: NamedJDBCTemplate
Stopwatch stopWatch3 = Stopwatch.createStarted();
Map<String, Object> paramMap = createNamedParameterMap(domain, dimensionMap);
Set<Long> rs3 = extractIdSet(getNamedParameterJdbcTemplate().queryForRowSet(sql, paramMap));
log.warn("NamedParameterJdbcTemplate took: {} ms", stopWatch3.elapsed(TimeUnit.MILLISECONDS));
}

这是结果。确切的时间因运行而异,但始终保持在相同的数量级。
  • 使用不带任何绑定(bind)参数的查询会很快完成,大约不到 100 毫秒。
  • 使用 Spring 的 JdbcTemplate使用 9 个绑定(bind)变量,性能下降到爬行,大约需要 4 秒。
  • 最后,使用 NamedJdbcTemplate ,最简单,最灵活,与案例 2 一样慢;至少,这并不奇怪,因为在窗帘后面 NamedJdbcTemplate将用命名参数替换我的查询到与案例 2 等效的内容。

  • 它没有获取连接,因为它们都是从同一个连接池中获取的。好像不是queryForRowSet()单独运行,因为这实际上也是最快的情况下使用的。同样,它看起来似乎与 Spring 的异常翻译或参与正在进行的事务没有任何关系,因为这也会影响案例 1。

    所以最后,问题是:为什么是 Spring 的 JdbcTemplate与没有绑定(bind)参数的普通语句相比,在这种情况下使用绑定(bind)参数非常慢?

    最佳答案

    原来它既不是JdbcTemplate也不是 NamedJdbcTemplate .它也不是关于 PreparedStatementStatement 相比,即使后者是最快的。那只是因为普通语句不带有绑定(bind)参数。如果我有没有绑定(bind)参数的查询,它与原始 JDBC 和 NamedJdbcTemplate 的速度大致相同。一样。

    我们的 Oracle 11g 只是为此查询选择了一个错误的执行计划,其中包含 9 个绑定(bind)参数,并且不管实际参数是什么都坚持执行计划。我不知道为什么,也没有真正的 DBA 可用。

    使用相同数据对 PostgreSQL 9.3 数据库进行的测试表明,无论有无绑定(bind)参数,它的速度都一样快;开箱即用的 Ubuntu 安装。

    关于java - 绑定(bind)参数慢,使用 JdbcTemplate 更慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37784388/

    相关文章:

    java - Spring @Cacheable 和 @Async 注解

    c# - ORA-21500 : internal error code, 参数 : [%s], [%s]、[%s]、[%s]、[%s]、[%s]、[%s]、[%s]

    Oracle:如何从 PL/SQL 使作业脱机?

    java - 如何避免在 Java 中检查空值?

    java - 将图像存储到 netbeans 的文件夹中

    java - 如何从 Java 中的纪元(1970-01-01)获得毫秒?

    java - 自动装箱:为什么 Short S1 = 100;编译正常,但 Long F1 = 100;失败的?

    java - 如何 EasyMock 对返回通配符泛型的方法的调用?

    java - 使 Spring bean 的行为类似于 ExecutorService 的 ThreadLocal 实例

    php - Codeigniter和Oracle获取插入查询的last_id