使用绑定参数很慢,使用 JdbcTemplate 更慢

Mic*_*fel 6 java oracle spring jdbc jdbctemplate

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

\n\n

考虑以下例程:

\n\n
private void getObjectIds(ObjectDomain domain, HashMap<String, List<String>> dimensionMap)\n    throws SQLException {\nString sql = "SELECT m2.OBJECT_ID"\n    + "  FROM MetaInformations m1, MetaInformations m2\\n"\n    + "  WHERE m1.OBJECT_ID = m2.OBJECT_ID\\n"\n    + "    AND m1.OBJECT_DOMAIN = :domain AND m1.KEY = :key1 AND\\n"\n    + "        m1.REF_OBJ_VALUE IN (:values1)\\n"\n    + "    AND m2.OBJECT_DOMAIN = :domain AND m2.KEY = :key2 AND\\n"\n    + "        m2.REF_OBJ_VALUE IN (:values2)";\nString sqlWithBind = "SELECT m2.OBJECT_ID\\n"\n    + "  FROM MetaInformations m1, MetaInformations m2\\n"\n    + "  WHERE m1.OBJECT_ID = m2.OBJECT_ID\\n"\n    + "    AND m1.OBJECT_DOMAIN = ? AND m1.KEY = ? AND\\n"\n    + "        m1.REF_OBJ_VALUE IN (?, ?, ?, ?)\\n"\n    + "    AND m2.OBJECT_DOMAIN = ? AND m2.KEY = ? AND\\n"\n    + "        m2.REF_OBJ_VALUE IN (?)";\n\n// Prebuilding statement, no bind variables left\nStopwatch stopWatch2 = Stopwatch.createStarted();\nIterator<Entry<String, List<String>>> entries = dimensionMap.entrySet().iterator();\nEntry<String, List<String>> entry1 = entries.next();\nEntry<String, List<String>> entry2 = entries.next();\nString prebuilt = sql.replace(":domain", "\'" + domain + "\'")\n    .replace(":key1", "\'" + entry1.getKey() + "\'")\n    .replace(":values1",\n        entry1.getValue().stream().map(s -> "\'" + s + "\'").collect(Collectors.joining(", ")))\n    .replace(":key2", "\'" + entry2.getKey() + "\'")\n    .replace(":values2",\n        entry2.getValue().stream().map(s -> "\'" + s + "\'").collect(Collectors.joining(", ")));\nSet<Long> rs2 = extractIdSet(getNamedParameterJdbcTemplate().queryForRowSet(prebuilt, Collections.emptyMap()));\nlog.warn("Prebuilt took: {} ms", stopWatch2.elapsed(TimeUnit.MILLISECONDS));\n\n// Simple JDBCTemplate with 9 bind parameters\nStopwatch stopWatch5 = Stopwatch.createStarted();\nSet<Long> rs1 = extractIdSet(getJdbcTemplate().queryForRowSet(sqlWithBind,\n    domain.toString(),\n    entry1.getKey(),\n    entry1.getValue().get(0),\n    entry1.getValue().get(1),\n    entry1.getValue().get(2),\n    entry1.getValue().get(3),\n    domain.toString(),\n    entry2.getKey(),\n    entry2.getValue().get(0)));\nlog.warn("JdbcTemplate took: {} ms", stopWatch5.elapsed(TimeUnit.MILLISECONDS));\n\n// Most beautiful: NamedJDBCTemplate\nStopwatch stopWatch3 = Stopwatch.createStarted();\nMap<String, Object> paramMap = createNamedParameterMap(domain, dimensionMap);\nSet<Long> rs3 = extractIdSet(getNamedParameterJdbcTemplate().queryForRowSet(sql, paramMap));\nlog.warn("NamedParameterJdbcTemplate took: {} ms", stopWatch3.elapsed(TimeUnit.MILLISECONDS));\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是结果。确切的时间因运行而异,但始终保持在相同的数量级。

\n\n
    \n
  1. 使用不带任何绑定参数的查询可以很快完成,大约不到 100 毫秒
  2. \n
  3. 使用带有 9 个绑定变量的 Spring\xe2\x80\x99s JdbcTemplate,性能会下降到爬行,大约需要 4 秒
  4. \n
  5. 最后,使用NamedJdbcTemplate最简单、最灵活的 a 与情况 2 一样慢;至少这并不奇怪,因为在幕后NamedJdbcTemplate将使用命名参数替换我的查询,并将其替换为与情况 2 等效的内容。
  6. \n
\n\n

它\xe2\x80\x99s 没有获取连接,因为它们都从同一个连接池获取连接。它似乎不是queryForRowSet()单独的函数,因为 \xe2\x80\x99 实际上也在最快的情况下使用。同样,它看起来似乎与 Spring\xe2\x80\x99 的异常转换或参与正在进行的事务有任何关系,因为这也应该影响情况 1。

\n\n

最后,问题是:JdbcTemplate与没有绑定参数的普通语句相比,为什么在这种情况下带有绑定参数的 Spring\xe2\x80\x99s 如此慢?

\n

Mic*_*fel 3

事实证明它既不是JdbcTemplate也不是NamedJdbcTemplatePreparedStatement它也与vs无关Statement,即使后者是最快的。那只是因为普通语句不带有绑定参数。如果我的查询没有绑定参数,它\xe2\x80\x99s 与原始 JDBC 等的速度大致相同NamedJdbcTemplate

\n\n

我们的 Oracle 11 g只是为这个具有 9 个绑定参数的查询选择了一个错误的执行计划,并且无论实际参数是什么,都坚持它。我不知道为什么,也没有真正的 DBA。

\n\n

在具有相同数据的 PostgreSQL 9.3 数据库上进行的测试表明,无论有没有绑定参数,它都同样快;带有开箱即用的 Ubuntu 安装。

\n