57

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

我们有一个 Java 应用程序,其中我们使用常规的“语句”比使用“PreparedStatement”更多。在尝试使用更多 PreparedStatements 的同时,我试图更全面地了解 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 是否仍然在我不知道的情况下被缓存和重用?

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

提前致谢。

4

10 回答 10

34

准备好的语句主要是关于性能的概念是一种误解,尽管它很常见。

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

因此,作为使用准备好的语句的一个原因,对 SQL 注入攻击的保护远远超过了性能提升。如果您不担心 SQL 注入攻击,您可能应该...

于 2009-03-26T21:49:58.987 回答
29

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

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 注入。在我的公司,这是一个面试问题;弄错了,我们可能不会雇用你。

于 2009-03-26T21:30:39.157 回答
18

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

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

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

于 2009-03-26T21:26:00.400 回答
8

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

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

于 2009-03-26T22:51:29.867 回答
4

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

我将从性能方面回答。这里的其他人已经规定了PreparedStatements 对 SQL 注入具有弹性(有福的优势)。

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

于 2014-12-11T10:30:35.597 回答
3

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

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

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

于 2009-03-26T21:31:33.227 回答
3

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

是的,至少对于 Oracle。根据 Oracle® 数据库 JDBC 开发人员指南隐式语句缓存(已添加重点),

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

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

于 2014-12-08T08:12:54.213 回答
2

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

于 2014-12-12T04:53:37.443 回答
2

简短的回答:

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

长答案:

根据Wikipedia,使用准备好的语句的典型工作流程如下:

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

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

执行:稍后,应用程序为参数提供(或绑定)值,DBMS 执行语句(可能返回结果)。应用程序可以使用不同的值多次执行该语句。在此示例中,它可能为第一个参数提供“面包”,为第二个参数提供“1.00”。

准备:

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

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

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

预编译:

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

执行:

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

结论:

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

  1. 阶段 1,准备和预编译,这个阶段预计会完成一次,并为性能增加一些开销。
  2. 阶段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
于 2016-01-20T09:49:03.680 回答
1

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

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

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

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

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

于 2014-12-11T00:51:16.667 回答