【问题标题】:Any performance benefit to PreparedStatement in simple, static method query?PreparedStatement 在简单的静态方法查询中有什么性能优势?
【发布时间】:2021-11-09 15:34:12
【问题描述】:

更新 2 反对票是应得的,并鼓励我编写一个更强大的测试:使用 java 1.8,ojdbc8.jar 对抗非生产,Oracle 12C我通常是唯一用户的数据库, 我运行了 100 次选择的 10 次迭代:50 次使用 PreparedStatement,50 次使用 Statement(使用字符串连接构建查询)。 为了尝试排除某种数据库缓存,我再次运行测试,但切换了顺序,首先运行语句,然后运行 ​​PreparedStatements。 无论顺序如何,性能没有显着差异。结果见下文(以毫秒为单位,带有平均值和标准偏差)。

更新:感谢您的所有意见。我更新了标题。我在研究这个时发现了很多陈词滥调(“PreparedStatements 总是更快”,“PreparedStatements 总是更慢”),但没有确定的结果,我最终编写了一个测试,发现对于这种情况,PreparedStatements 始终如一~慢 25%。

最初的问题: 我有一个带有简单查询的静态方法,用于检查数据库中是否已经存在 ID:

public static boolean isPersisted(Integer id) {
   String sql = "select id from table where id = ?;

我知道它们可以防止 SQL 注入,并且我知道它们在循环插入带有 ID 列表的插入时提供了很大的好处(例如)但是,是否有任何 性能/内存优势在这里使用准备好的语句?

【问题讨论】:

  • PreparedStatement 可用于应用程序的整个生命周期。数据库不必每次都重新解析查询
  • 你问错了问题:不要养成用这样的字符串连接构建 SQL 查询的习惯,你不会因为几年后有人利用漏洞而丢掉工作......性能和内存方面的考虑几乎总是远远低于维护系统的完整性。
  • 一些数据库服务器维护已解析语句的缓存,因此以id = ? 样式重复运行应该重新使用已解析语句(并且可能是执行计划)。如果您运行 1000 次 id = actualvalue,那么服务器可能会丢弃其他(可能更复杂的评估)应用程序 SQL,并且您的错误代码的影响可能在其他地方很明显。优秀的数据库管理员可能会发现这一点并要求修复。
  • 你的例子不是静态查询,是参数化的。

标签: java jdbc prepared-statement


【解决方案1】:

在此处使用准备好的语句是否有任何性能/内存优势?

当您必须使用不同的数据多次运行相同的语句时,准备好的语句会快得多。这是因为 SQL 只会验证一次查询,而如果你只使用一条语句,它会每次都验证查询。

原因和发生的细节可以找到in this post

【讨论】:

    【解决方案2】:

    如果查询只执行一次,并且如果id 是为应用程序本身干净地生成的(例如作为简单的计算结果),则准备好的语句没有任何好处。

    如果查询必须使用不同的id 值多次执行,则使用准备好的查询将获得较高的性能提升,因为该查询只会被 sql 引擎解析一次。

    如果id 参数来自用户界面,您必须使用准备好的语句,除非您打算容易受到SQL Injection 的攻击。长话短说,它曾经是针对编码不良的 Web 应用程序的常见攻击,允许攻击者执行任意 SQL 命令。

    【讨论】:

      【解决方案3】:

      基本准备好的语句不易受到 SQL 注入的影响,并且查询响应时间更快。

      我认为您正在寻找这样的东西:

      public static boolean isPersisted(Integer id) {
      String sql = "select id from table where id = ?";
       
      PreparedStatement ps = con.prepareStatement(sql);
      ps.setInt(1, id);
      
      

      【讨论】:

        【解决方案4】:

        PreparedStatements 已预编译。这意味着经过验证、转义和缓存(有关PreparedStatement 预编译的更多详细信息,请参见此问题What does it mean when I say Prepared statement is pre-compiled?

        预编译意味着所有工作只完成一次,避免了每次执行查询所需的所有成本。

        但与往常一样,如果使用得当,魔法就会完成。您可以使用String 连接创建PreparedStatement,但请注意,最终字符串中的任何更改都会导致新的预编译(从而失去好处)。

        SQL 注入也是如此。始终使用占位符?。如果你在PreparedStatement 中连接字符串(没有什么能阻止你做这样的事情)你很容易陷入 SQL 注入,例如:

        connection.prepareStatement("Select * from MY_TABLE where email = '" + userInputEmail + "'")
        

        切勿进行上述操作。是 PreparedStatement,但可以被 SQL 注入。

        因此,要从PreparedStatements 中受益,请正确使用它们。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-02-05
          • 2012-05-02
          • 2012-01-16
          • 1970-01-01
          • 2014-10-15
          相关资源
          最近更新 更多