“prepareStatement(String sql)”的内部功能

时间:2017-11-08 14:01:53

标签: java oracle jdbc prepared-statement rdbms

我已阅读prepareStatement(String sql)的说明以及与PreparedStatement的性能和缓存相关的大量帖子,我很清楚数据库将解析并编译预准备语句的查询,以便随后相同的查询,另一轮解析和编译不会发生,但我不清楚是否:

  • 每次调用prepareStatement(String sql)都会导致数据库调用?我想会的。
  • 每次调用prepareStatement(String sql)都会导致数据库调用,然后我不明白这行来自docs是什么意思 - “有或没有IN参数的SQL语句可以预先编译并存储在一个PreparedStatement对象。“?因为我们得到的真正的性能优势是在RDBMS方面,PreparedStatement对象在做什么呢?
  • 假设我使用PreparedStatement对象执行了一个查询(简单的SELECT,没有参数化),现在如果我使用Statement对象执行相同的查询,那么RDBMS是否仍会解析和编译或不会?

3 个答案:

答案 0 :(得分:2)

以下内容仅适用于Oracle数据库JDBC驱动程序。其他驱动因素可能不同。

对Connection.prepareStatement(String)的调用不会进行数据库往返。它构造一个新的PreparedStatement并将SQL String存储在其中。就是这样。

即使未使用的PreparedStatements也是中等复杂的,所以这不是一个完全无关紧要的电话。即便如此,缓存未使用的PreparedStatements也没什么价值。建造新建筑的成本很低。强调“未使用”。第一次执行PreparedStatement会执行完整的往返并完成构建PreparedStatement。使用PreparedStatement一旦重用它的成本远远低于创建新的并且第一次使用它的成本。

对于客户端和服务器,在两个PreparedStatements中执行相同的SQL比执行两次PreparedStatement要贵。对于客户来说,额外的成本非常简单。这是完全构建第二个PreparedStatement的成本,这包括客户在第一次执行往返之后所做的工作。数据库中的事情并非如此简单。

Oracle数据库具有多级缓存和重用,可最大程度地降低多次执行给定SQL字符串的成本。 PreparedStatement包含游标ID。该id指的是服务器中的游标。游标是复杂的结构,是数据库表示SQL执行的表示。某些结构可以由执行相同SQL的其他游标共享。一些结构对于单次执行是唯一的。某些结构可以在执行SQL而不是其他游标的某些游标之间共享。这是一个复杂的系统。

作为一般规则,创建新的PreparedStatement需要进行硬解析。如果服务器之前已经看过SQL,则硬解析可能不是完整的硬解析,因为服务器可以重用某些游标结构。如果应用程序重新执行PreparedStatement,那么理想情况下服务器不必对游标执行任何操作;它只是重新执行它。但是在很多情况下服务器必须先进行软解析才能重新执行。软解析比硬解析工作少,但不是微不足道。

以上忽略了隐式语句缓存。 Implicit Statement Cache存储通过执行Prepared和Callable语句创建的结构。如果应用程序执行SQL,则关闭PreparedStatement,然后使用相同的SQL创建新的PreparedStatement,重新使用第一次执行创建的结构。首次执行缓存中的PreparedStatement的成本与所有实际目的相同,与重新执行相同的PreparedStatement相同。

答案 1 :(得分:1)

  1. 是的,每次调用prepareStatement都会导致数据库调用。如果不是,那么可能是这种情况:

    execute("create table x(y integer)");
    prepareStatement("select * from x"); // #1
    execute("rename table x to old_x");
    execute("create table x(z varchar(100))");
    prepareStatement("select * from x"); // #2 - this stamenent is not equal to previous
    
  2. PreparedStatement通常是游标句柄的包装器。 prepareStatement将SQL语句发送到RDBMS。 RDBMS编译它并返回它的句柄。下一次数据库调用使用此句柄,因此RDBMS将使用编译语句。

  3. 这取决于RDBMS。比方说,在这种情况下,Oracle将使用“软解析”。软解析意味着比数据库在其缓存中定位相等的语句并尽可能使用它。它比重新编译更有效,但比使用预准备语句更有效。

答案 2 :(得分:0)

虽然@Sanders和@Douglas很好但是它们不完整(即使只考虑JDBC驱动程序),所以我也提出了我的答案,这也是不完整的,我建议阅读所有3个答案以获得良好的知识:

PreparedStatement的性能优势

  • PreparedStatement对象最适合动态查询,也可以避免SQL注入。
  • 使用PreparedStatement进行缓存:
    1. 在数据库级缓存:
      • 在数据库级别的缓存中,当您使用带有PreparedStatement对象的参数化查询时,对于第一次执行,db服务器将解析并编译查询并缓存其执行计划,现在当再次出现相同的参数化查询时,所有这些都需要不要再这样做,这样你就可以获得性能优势(请注意,为什么你应该更喜欢参数化查询而不是“+”基于运算符的查询)。
      • 因此,底线是数据库服务器可以缓存查询,以便它可以避免解析,编译和识别执行计划时间。
    2. J2EE服务器级别的缓存:
      • 现在,在我开始重要的事情之前要注意的是,这个缓存只是在J2EE服务器的情况下,如果你有一个独立的Java程序,那么你就无法得到这个缓存。
      • 现在,在JEE服务器的情况下,你得到一个池连接对象,现在当你从它创建一个预准备语句对象时,JEE服务器将为这个数据库连接缓存这个准备好的语句对象(现在,重要的是要注意这里是在JEE服务器的情况下,当您在连接对象上调用close方法时,与数据库服务器的真实连接将不会被关闭,它只会关闭代理/包装器连接对象,所以我想如果你已经为连接设置了一些属性对象然后它仍然会在那里),所以当相同的连接对象返回到应用程序时,如果情况相同的查询与准备好的语句对象一起使用,那么JEE服务器将跳过到db服务器的往返,因此您将获得性能优势
  • PreparedStatements从性能角度看是好的,因为您可以使用PreparedStatement编译查询,然后使用此对象传递不同的参数。现在,重要的是将它与Statement对象的用法进行对比 - 你不能使用Statement对象设置或传递参数,所以每次你必须创建一个新的语句对象(这意味着往返于db服务器)然后执行它(执行它)表示另一次往返db服务器的往返)。现在,如果您看到PreparedStatement的情况,那么您可以通过指定查询参数(这意味着往返于数据库服务器)创建PreparedStatement对象,然后在此对象上设置不同的参数并执行它,现在优势到达此处是你将只构造一次PreparedStatement对象,这意味着只有一次往返数据库服务器的往返,因此可以节省往返数据库服务器的往返,以便创建对象,这是Statement对象的情况。
  • 使用参数化查询与基于“+”运算符的查询
    • 现在,据说您应该使用参数化查询和PreparedStatement对象而不是基于“+”运算符的查询,这是正确的,但重要的是要注意的是,不会出现性能提升的情况,一旦你使用PreparedStatement对象而不是Statement对象,你就可以避免往返数据库服务器进行对象创建,因此肯定有好处。但是使用“+”运算符的缺点是在数据库服务器端,如果使用“+”运算符,则db服务器将无法缓存在使用参数化查询时将发生的查询。
  • 另一个需要注意的重要事项是当你调用connection. prepareStatement()然后如果它是一个独立的Java程序,那么如果它是一个J2EE服务器就会发生到服务器的往返由于JEE PreparedStatement缓存
  • ,可能不会发生到服务器的往返