【问题标题】:PreparedStatements and performancePreparedStatements 和绩效
【发布时间】:2010-10-15 19:28:33
【问题描述】:

所以我一直听说 PreparedStatements 对性能有好处。

我们有一个 Java 应用程序,在该应用程序中,我们使用常规的“语句”比使用“PreparedStatement”要多。在尝试使用更多 PreparedStatement 的同时,我试图更全面地了解 PreparedStatements 的工作原理——在客户端和服务器端。

那么如果我们有一些典型的 CRUD 操作并且在应用程序中重复更新一个对象,使用 PS 是否有帮助?我知道我们每次都必须关闭 PS,否则会导致游标泄漏。

那么它对性能有何帮助?驱动程序是否缓存预编译语句并在我下次执行 connection.prepareStatement 时给我一份副本?或者数据库服务器有帮助吗?

我理解有关 PreparedStatements 的安全优势的争论,我很欣赏下面强调这一点的答案。不过,我真的希望将讨论集中在 PreparedStatements 的性能优势上。

更新:当我说更新数据时,我的真正意思是更多地随机调用该方法多次。我理解下面提供的答案的优势,它要求在循环中重用语句。

    // some code blah blah
    update();

    // some more code blah blah 
    update();

.... 

public void update () throws SQLException{
 try{
      PreparedStatement ps = connection.prepareStatement("some sql");
      ps.setString(1, "foobar1");
      ps.setString(2, "foobar2");
      ps.execute();
 }finally {
     ps.close();

 }

}

没有办法真正重用“ps”java 对象,我知道实际的 connection.prepareStatement 调用非常昂贵。

这让我回到了最初的问题。这个“一些 sql”PreparedStatement 是否仍然在我不知道的情况下被缓存和重用?

我还应该提到我们支持多个数据库。

提前致谢。

【问题讨论】:

  • 如果你没有使用 PrepardStatements 并且不担心 SQL 注入攻击,那么你对 SQL 注入攻击的了解还不够。
  • 不仅是 sql 注入,还减少了将类型作为字符串传递、转义特殊字符等的错误。另外,在使用完 PS 后关闭 PS,就像其他所有操作一样,这通常是在“批次”更新结束时。游标泄漏通常是由于打开 ResultSet 造成的。
  • +1 个好问题和几个好答案。对于 Oracle,预编译语句的巨大“性能”优势来自于数据库服务器,而不是单个语句的执行,而是相同 SQL 文本的重复执行。单次执行新语句不会有任何性能提升。当第二次、第三次执行该语句时,就会节省 CPU 周期。 Oracle 跳过了“硬解析”的所有繁重工作,并重用了之前准备好的查询计划。
  • 哎呀,5年后,感觉这里仍然没有一个好的答案。特别是,我认为回答关于在使用不同的连接反复准备相同的查询时是否获得任何性能优势的部分是关键。我将悬赏赏金,以获得对此问题的良好、深入的回答。
  • 你在问题​​中说:“没有办法真正重用'ps'java对象”。为什么不?这正是您实现性能提升的方式。

标签: java database performance prepared-statement


【解决方案1】:

Prepared statements 主要是关于性能的概念是一种误解,尽管它很常见。

另一位发帖人提到,他注意到 Oracle 和 SQL Server 的速度提高了约 20%。我注意到 MySQL 有一个类似的数字。事实证明,解析查询并不是所涉及工作的重要部分。在一个非常繁忙的数据库系统上,查询解析是否会影响整体吞吐量也不清楚:总的来说,它可能只是用尽了 CPU 时间,否则在数据从磁盘返回时会处于空闲状态。

因此,作为使用准备好的语句的一个原因,防止 SQL 注入攻击远远超过性能改进。如果您不担心 SQL 注入攻击,您可能应该...

【讨论】:

  • 我打算添加 SQL 注入保护,但我会 +1 你!
  • “它可能只是用完 CPU 时间,否则当数据从磁盘返回时会处于空闲状态”——这取决于所涉及的系统和查询。我使用过 CPU 资源适中但并行磁盘容量巨大的数据库系统。可以说该系统设计得很糟糕,但该系统肯定受 CPU 限制,因此我们可以采取一些措施来提高 CPU 利用率(例如减少查询解析)确实有所作为。
  • 我不相信sql性能提升只有%20。这方面的基准在哪里?但是你关于 sql 注入的观点是一个很好的观点。我仍然认为说性能提升只有 %20 是一种误导
  • 另一方面,prepared statements 可能永远不会比原始查询慢,并且它们增加了额外的安全性,所以大多数时候,很难想象你为什么不直接使用它们,无论结果是什么精确的性能增益。
  • @sactiw 当然,这些是特定数据库引擎/版本的详细信息,但例如您可以在此处阅读最近版本的 SQL Server 使用的一些标准:msdn.microsoft.com/en-us/library/cc293624.aspx 与任何缓存资源一样,从缓存中删除给定计划的决定本质上是多种因素的组合:释放空间的需要,缓存的计划有多有用,以及首先创建该计划需要多少努力。在回答您的第一个问题时,它们似乎不是特定于连接的。
【解决方案2】:

在重复使用您准备的相同语句时,准备好的语句可以提高性能:

PreparedStatement ps = connection.prepare("SOME SQL");

for (Data data : dataList) {
  ps.setInt(1, data.getId());
  ps.setString(2, data.getValue();
  ps.executeUpdate();
}

ps.close();

这比在循环中创建语句要快得多。

一些平台还缓存准备好的语句,这样即使关闭它们也可以更快地重建。

但是,即使性能相同,您仍然应该使用准备好的语句来防止 SQL 注入。在我的公司,这是一个面试问题;弄错了,我们可能不会雇用你。

【讨论】:

  • 它们也会影响性能。如果它们是由 ORM 生成并且没有被重用,那么某些数据库(例如 Oracle)实际上会比原生查询运行得更慢。在使用 regexp_like 或类似运算符时尤其如此。有时会生成不同的查询计划。
【解决方案3】:

准备好的语句在第一次使用后确实会被缓存,这就是它们在性能方面优于标准语句的原因。如果您的陈述没有改变,那么建议使用此方法。它们通常存储在语句缓存中以供更改使用。

更多信息可以在这里找到:

http://www.theserverside.com/tt/articles/article.tss?l=Prepared-Statments

您可能希望将 Spring JDBCTemplate 视为直接使用 JDBC 的替代方案。

http://static.springframework.org/spring/docs/2.0.x/reference/jdbc.html

【讨论】:

  • @Jon:只是要指出,对于 Oracle,在服务器端,所有语句都以相同的方式准备。所有语句都被“缓存”并可供重用。 “诀窍”是一遍又一遍地执行完全相同的 SQL 文本,只是使用不同的提供值(绑定参数)执行
  • 非常有用的链接,尤其是第二个。
  • 仔细阅读后,我认为本文中的链接给出了最好的解释。我希望我们在我们的应用程序中看到这些好处,但这是一个不同的话题。在这里授予赏金以供参考,尽管它可能是旧的。
【解决方案4】:

解析 SQL 并不是唯一要做的事情。验证表和列确实存在,创建查询计划等。您使用 PreparedStatement 支付一次。

绑定以防止 SQL 注入确实是一件非常好的事情。还不够,海事组织。您仍然应该在进入持久层之前验证输入。

【讨论】:

  • 任何半体面的数据库都会在没有准备好的语句/存储过程的情况下缓存查询的一般形式。
  • 我认为主要是你保存的解析。查询计划至少部分取决于查询的参数,而不仅仅是它的形式(尽管我接受可能会缓存某种“部分计划”)。
【解决方案5】:

那么它对性能有何帮助?驱动是否缓存 预编译语句,下次我做的时候给我一份副本 connection.prepareStatement?或者数据库服务器有帮助吗?

我会根据性能来回答。这里的其他人已经规定PreparedStatements 对 SQL 注入有弹性(幸好)。

应用程序(JDBC 驱动程序)创建 PreparedStatement 并将其传递给带有占位符的 RDBMS(?)。 RDBMS 预编译,应用接收到的PreparedStatement 的查询优化(如果需要)并且(在某些情况下)通常缓存它们。在执行PreparedStatement 期间,使用预编译的PreparedStatement,将每个占位符替换为其相关值并进行计算。这与编译并直接执行它的Statement 形成对比,PreparedStatement 编译和优化查询仅一次。现在,上面解释的这种情况并不是所有 JDBC 供应商的绝对情况,但本质上这就是 PreparedStatement 的使用和操作方式。

【讨论】:

    【解决方案6】:

    有趣的是:几年前,我在 Java 1.4 中使用 ODBC 对准备好的语句和动态语句进行了一些实验,同时使用了 Oracle 和 SQL Server 后端。我发现对于某些查询,prepared statements 可能会快 20%,但对于哪些查询改进到何种程度存在特定于供应商的差异。 (这应该不足为奇,真的。)

    底线是,如果您将重复使用相同的查询,准备好的语句可能有助于提高性能;但是,如果你的表现很糟糕以至于你需要立即采取行动,不要指望使用准备好的陈述来给你带来根本的提升。 (20% 通常没什么好写的。)

    当然,您的里程可能会有所不同。

    【讨论】:

    • 性能基准是轶事。对于复杂的查询,我的猜测是性能提升可能更多。还要考虑到,如果你不使用准备好的语句,数据库查询缓存会很快填满。
    • 20% 可能是巨大的,因为这意味着服务器在此期间使用的资源也更少。乘以它,这很有意义。
    【解决方案7】:

    这让我回到了最初的问题。这个“一些 sql”PreparedStatement 是否仍然在我不知道的情况下被缓存和重用?

    是的,至少对于 Oracle。根据 Oracle® 数据库 JDBC 开发人员指南 Implicit Statement Caching(已添加重点),

    启用隐式 Statement 缓存后,当您调用该语句对象的 close 方法时,JDBC 会自动缓存准备好的或可调用的语句。使用标准连接对象和语句对象方法缓存和检索准备好的和可调用的语句。

    普通语句不会被隐式缓存,因为隐式语句缓存使用 SQL 字符串作为键,而普通语句是在没有 SQL 字符串的情况下创建的。因此,隐式语句缓存仅适用于使用 SQL 字符串创建的 OraclePreparedStatementOracleCallableStatement 对象。您不能将隐式语句缓存与 OracleStatement 一起使用。当您创建 OraclePreparedStatementOracleCallableStatement 时,JDBC 驱动程序会自动在缓存中搜索匹配语句

    【讨论】:

    • 我认为这部分回答了这个问题——至少对于 Oracle 而言。似乎没有完全回答的部分问题是关于性能影响。我们刚刚使用带有 SQL Server 2005 的 JDBC 进行了一些测试,发现 PreparedStatements 始终比未准备好的语句多花 50% 以上的时间——即使重复执行完全相同的查询也是如此!如果你问我,那就太可怕了。另一个问题是JDBC缓存是依赖于数据库缓存还是独立的。
    • 微基准测试是臭名昭著的,你的 JDBC 驱动程序可能是罪魁祸首。此外,您需要更具体地了解您的实施。你比较究竟如何?
    • 我同意并且不想将任何重点放在我们所做的微基准测试上(这可能仅适用于我们的具体案例)。如果我们在这里找到任何东西,我可能会在博客上讨论该主题并深入研究基准测试。
    【解决方案8】:

    1. PreparedStatement 允许您编写动态和参数查询

    通过在 Java 中使用 PreparedStatement,您可以编写参数化的 sql 查询并使用相同的 sql 查询发送不同的参数,这比创建不同的查询要好得多。

    2。 PreparedStatement 比 Java 中的 Statement 更快

    使用 PreparedStatement 的主要好处之一是性能更好。 PreparedStatement 被预编译 在数据库中,访问计划也缓存在数据库中,这允许数据库执行使用准备好的语句编写的参数查询比普通查询快得多,因为它要做的工作更少。您应该始终尝试在生产 JDBC 代码中使用 PreparedStatement 来减少数据库负载。为了获得性能优势,值得注意的是只使用参数化版本的 sql 查询而不是字符串连接

    3. PreparedStatement 可防止 Java 中的 SQL 注入攻击

    阅读更多:http://javarevisited.blogspot.com/2012/03/why-use-preparedstatement-in-java-jdbc.html#ixzz3LejuMnVL

    【讨论】:

      【解决方案9】:

      简答:

      PreparedStatement 有助于提高性能,因为数据库客户端通常会重复执行相同的查询,这使得对初始查询进行一些预处理成为可能加快后续重复查询强>。

      长答案:

      根据Wikipedia,使用prepared statement的典型工作流程如下:

      准备:报表模板由应用程序创建并发送 到数据库管理系统(DBMS)。保留某些值 未指定的,称为参数、占位符或绑定变量 (下面标有“?”):INSERT INTO PRODUCT(名称、价格)值(?、?)

      (预编译):DBMS 解析、编译并执行查询优化 语句模板,并存储结果而不执行它。

      执行:稍后,应用程序提供(或绑定)值 对于参数,DBMS 执行语句(可能 返回结果)。应用程序可以执行尽可能多的语句 根据需要使用不同的值。在这个例子中,它可能 为第一个参数提供“面包”,为第二个参数提供“1.00” 参数。

      准备:

      在 JDBC 中,“准备”步骤是通过调用 java.sql.Connection.prepareStatement(String sql) API 来完成的。根据其Javadoc:

      此方法针对处理受益于预编译的参数化 SQL 语句进行了优化。如果驱动程序支持预编译,prepareStatement 方法会将语句发送到数据库进行预编译。某些驱动程序可能不支持预编译。在这种情况下,在执行 PreparedStatement 对象之前,该语句可能不会发送到数据库。这对用户没有直接影响;但是,它确实会影响哪些方法抛出某些 SQLException 对象。

      由于调用此 API 可能会将 SQL 语句发送到数据库,因此通常是一个昂贵的调用。根据 JDBC 驱动的实现,如果你有相同的 sql 语句模板,为了更好的性能,你可能不得不避免在客户端为同一个 sql 语句模板多次调用这个 API。

      预编译:

      发送的语句模板将在数据库中预编译并缓存在数据库服务器中。数据库可能会使用连接和 sql 语句模板作为键,将预编译的查询和计算的查询计划作为缓存中的值。解析查询可能需要验证要查询的表、列,因此可能是一个昂贵的操作,query plan 的计算也是一个昂贵的操作。

      执行:

      对于来自同一个连接和sql语句模板的后续查询,数据库服务器将直接从缓存中查找预编译的查询和查询计划,而无需重新计算。

      结论:

      从性能的角度来看,使用 prepare 语句是一个两阶段的过程:

      1. 第 1 阶段,准备和预编译,此阶段预计为 完成一次并为性能增加一些开销。
      2. 第二阶段, 重复执行相同的查询,因为阶段 1 有一些预 查询的处理,如果重复查询的数量很大 足够了,这可以节省大量的预处理工作 查询。

      如果你想了解更多细节,有一些文章解释了 PrepareStatement 的好处:

      1. http://javarevisited.blogspot.com/2012/03/why-use-preparedstatement-in-java-jdbc.html
      2. http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html

      【讨论】:

        【解决方案10】:

        Prepared statements 在性能方面相对于普通语句具有一些优势,具体取决于您如何使用它们。正如之前有人所说,如果您需要使用不同的参数多次执行相同的查询,您可以重用准备好的语句并只传递新的参数集。性能提升取决于您使用的特定驱动程序和数据库。

        例如,在数据库性能方面,Oracle 数据库在每次计算后缓存一些查询的执行计划(并非所有版本和所有 Oracle 配置都是如此)。即使您关闭一个语句并打开一个新语句,您也可以找到改进,因为这是在 RDBMS 级别完成的。仅当两个后续查询(逐个字符)相同时,才会激活这种缓存。这不适用于普通语句,因为参数是查询的一部分并产生不同的 SQL 字符串。

        其他一些 RDBMS 可能更“智能”,但我不希望它们会使用复杂的模式匹配算法来缓存执行计划,因为它会降低性能。您可能会争辩说,执行计划的计算只是查询执行的一小部分。对于一般情况,我同意,但是..这取决于。请记住,通常情况下,计算执行计划可能是一项昂贵的任务,因为 rdbms 需要查阅内存外数据,例如统计信息(不仅是 Oracle)。

        然而,关于缓存的争论范围从执行计划到提取过程的其他部分。多次向 RDBMS 提供相同的查询(无需深入了解特定实现)有助于在 JDBC(驱动程序)或 RDBMS 级别识别已计算的结构。如果您现在在性能方面没有发现任何特别的优势,您不能排除性能改进将在驱动程序/rdbms 的未来/替代版本中实现。

        在批处理模式下使用准备好的语句可以获得更新的性能改进,但这是另一回事。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-08-29
          • 1970-01-01
          • 2023-04-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-03-14
          相关资源
          最近更新 更多