准备好的声明和表现

时间:2009-03-26 21:10:15

标签: java database performance prepared-statement

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

我们有一个Java应用程序,我们使用常规'Statement'比使用'PreparedStatement'更多。在尝试使用更多PreparedStatements时,我试图更全面地了解PreparedStatements如何工作 - 在客户端和服务器端。

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

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

我理解关于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是否仍然被缓存并在我不知道的封面下重用?

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

提前致谢。

10 个答案:

答案 0 :(得分:29)

准备好的陈述主要是关于表现的概念是一种误解,尽管这是一个非常普遍的陈述。

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

因此,作为使用预准备语句的一个原因,针对SQL注入攻击的保护远远超过性能改进。如果你不担心SQL注入攻击,你可能应该......

答案 1 :(得分:27)

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

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

答案 2 :(得分:13)

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

可在此处找到更多信息:

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

答案 3 :(得分:8)

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

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

答案 4 :(得分:4)

  

那么它对性能有何帮助?驱动程序是否缓存了   预编译的声明,并在下次我给我一份   各种Connection.prepareStatement?或者DB服务器有帮助吗?

我会回答性能问题。这里的其他人已经规定PreparedStatement对SQL注入具有弹性(有福的优势)。

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

答案 5 :(得分:3)

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

最重要的是,如果您将重复使用相同的查询,准备好的语句可能有助于提高性能;但如果你的表现足够糟糕,你需要立即采取行动,不要指望使用准备好的陈述给你一个激进的推动。 (20%通常没什么好写的。)

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

答案 6 :(得分:3)

这是让我回到原始问题的原因。这个“一些sql”PreparedStatement是否仍然被缓存并在我不知道的封面下重用?

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

  

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

     

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

答案 7 :(得分: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

答案 8 :(得分:1)

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

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

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

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

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

答案 9 :(得分:1)

简答:

PreparedStatement有助于提高性能,因为通常数据库客户端会重复执行相同的查询,这样就可以对初始查询执行一些预处理,以加快以下重复查询的速度

答案很长:

根据Wikipedia,使用预准备陈述的典型工作流程如下:

  

准备:语句模板由应用程序创建并发送   到数据库管理系统(DBMS)。剩下某些值   未指定的,称为参数,占位符或绑定变量   (下面标有“?”):插入产品(名称,价格)价值(?,?)

     

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

     

执行:稍后,应用程序提供(或绑定)值   对于参数,DBMS执行语句(可能   返回结果)。应用程序可以执行多个语句   因为它想要不同的价值。在这个例子中,它可能   为第一个参数提供'Bread',为第二个参数提供'1.00'   参数。

<强>准备:

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

  

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

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

<强>预编译:

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

<强>执行:

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

<强>结论:

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

  1. 阶段1,准备和预编译,预计这个阶段 完成一次并为性能添加一些开销。
  2. 第2阶段, 重复执行相同的查询,因为阶段1有一些前置 如果重复查询的数量很大,则处理查询 足够,这可以节省大量的预处理工作 查询。
  3. 如果你想了解更多细节,有一些文章解释了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