适用于以下版本: Dev (3.21) | 最新版 (3.20) | 3.19 | 3.18 | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11

重复语句

适用于 ✅ 开源版   ✅ 专业版   ✅ 企业版

处理此事件的SPI方法是duplicateStatements()。如果该功能可用,则将使用基于模式的SQL转换来进一步规范化可能重复的SQL。

在具有执行计划缓存(或“游标缓存”等)的数据库中,静态语句或仅因“不相关”的事情而不同的预处理语句是常见的开销来源

  • 它们可能因空格而异
  • 它们可能因不相关的语法元素而异(例如,多余的括号、对象名称限定、表别名等)
  • 它们可能因输入值而异,输入值被内联到语句中,而不是作为绑定值进行参数化
  • 它们可能因其IN谓词的IN列表大小而异

为什么这不好?

有两个主要问题

  • 如果重复的SQL出现在动态SQL中(即在生成的SQL中),则表明数据库的解析器和优化器可能必须做过多的工作来解析各种相似(但不完全相同)的SQL查询,并为它们找到一个执行计划。 实际上,在大多数情况下它会找到相同的执行计划,但会产生一些明显的开销。 根据查询的复杂性,此开销很容易从几毫秒变为几秒钟,从而阻塞数据库中的重要资源。 如果在高峰负载时出现重复的SQL,则此问题可能会对生产产生重大影响。 它永远不会影响您的(单用户)开发环境,在这些环境中解析重复SQL的开销是可管理的。
  • 如果重复的SQL出现在静态SQL中,则可能只是表明该查询已被复制粘贴,您可以重构它。 可能没有任何由重复静态SQL引起的性能问题

这里给出了一个例子

// A custom DiagnosticsListener SPI implementation
class DuplicateStatements implements DiagnosticsListener {
    @Override
    public void duplicateStatements(DiagnosticsContext ctx) {

        // The statement that is being executed and which has duplicates
        System.out.println("Actual statement: " + ctx.actualStatement());

        // A normalised version of the actual statement, which is shared by all duplicates
        // This statement has "normal" whitespace, bind variables, IN-lists, etc.
        System.out.println("Normalised statement: " + ctx.normalisedStatement());

        // All the duplicate actual statements that have produced the same normalised
        // statement in the recent past.
        System.out.println("Duplicate statements: " + ctx.duplicateStatements());
    }
}

然后

// Utility to run SQL on a new JDBC Statement
void run(String sql) {

    // Configuration is configured with the target DataSource, SQLDialect, etc. for instance Oracle.
    try (Connection c = DSL.using(configuration.derive(new DuplicateStatements()))
                           .diagnosticsConnection();
         Statement s = c.createStatement();
         ResultSet rs = s.executeQuery(sql)) {

        while (rs.next()) {
            // Consume result set
        }
    }
}
    // Everything is fine with the first execution
    run("SELECT title FROM book WHERE id = 1");

    // This query is identical to the previous one, differing only in irrelevant white space
    run("SELECT title FROM book WHERE  id = 1");

    // This query is identical to the previous one, differing only in irrelevant additional parentheses
    run("SELECT title FROM book WHERE (id = 1)");

    // This query is identical to the previous one, differing only in what should be a bind variable
    run("SELECT title FROM book WHERE id = 2");

    // Everything is fine with the first execution of a new query that has never been seen
    run("SELECT title FROM book WHERE id IN (1, 2, 3, 4, 5)");

    // This query is identical to the previous one, differing only in what should be bind variables
    run("SELECT title FROM book WHERE id IN (1, 2, 3, 4, 5, 6)");
}

与检测重复语句不同,重复语句统计信息是在所有JDBC连接和数据源上全局执行的。

反馈

您对此页面有任何反馈吗? 我们很乐意听到您的反馈!

The jOOQ Logo