oracle解析器如何处理序列

时间:2012-12-11 18:17:59

标签: oracle sequence

为什么oracle总是在查询下解析?

select MY_SEQUENCE_SEQ.nextval
from dual

Quest SQL Optimizer(8.6.0)的SGA统计信息:

  

处决:83630

     

Parse_calls:83630

序列详情:

  • 上次缓存值:1
  • 增加:1
  • 缓存大小:20
  • 周期:否
  • 订单:否

测试场景:

  1. 创建音序器:

    CREATE SEQUENCE MY_SEQUENCE_SEQ
      START WITH 1
      MAXVALUE 999999999999999999999999999
      MINVALUE 1
      NOCYCLE
      CACHE 20
      NOORDER;
    
  2. 对符合v $ sql视图的用户执行此查询。

    select executions, 
           parse_calls 
      from v$sql 
     where sql_text like 'select MY_SEQ%';`
    
  3. 执行序列n次

  4. 的查询
  5. 从第2点执行查询。

  6. 获得的结果:

    EXECUTIONS  - n
    PARSE_CALSS - n
    

    经过测试:

    数据库:Oracle数据库10g版本10.2.0.4.0 - 64位生产

    客户:Toad版本11.5.1.2

2 个答案:

答案 0 :(得分:3)

这不是Oracle的错,它就是TOAD将SQL发送到oracle的方式。即toad不会将语句句柄缓存到oracle,它只是在完成时关闭它。

当查询发送到SQL引擎时,Oracle将对查询执行三个主要操作之一。

  1. 难以解析
  2. 软解析
  3. 不解析
  4. 即我们想要处于案例3,我们当然不希望成为案例1! 那么每个案件何时会发生?

    当SQL根本不在共享池中或SQL在共享池中但是使用的绑定可变数据/文字意味着当前的SQL不可用时,将发生硬解析。例如,假设我们发布了这个SQL三次select MY_SEQUENCE_SEQ.nextval from dual。这将在Oracle第一次看到这个SQL并将其放入共享池时解析,并在第二次和第三次调用时进行软解析。我们可以很容易地看到这种情况:

    SQL> select n.name, s.value from v$mystat s, v$statname n where n.statistic# = s.statistic# and n.name in ('parse count (hard)', 'parse count (total)');
    
    NAME                      VALUE
    -------------------- ----------
    parse count (total)         522
    parse count (hard)          287
    
    SQL> select /* test1 */ MY_SEQUENCE_SEQ.nextval
      2  from dual;
    
       NEXTVAL
    ----------
            62
    
    SQL> select /* test1 */ MY_SEQUENCE_SEQ.nextval
      2  from dual;
    
       NEXTVAL
    ----------
            63
    
    SQL> select /* test1 */ MY_SEQUENCE_SEQ.nextval
      2  from dual;
    
       NEXTVAL
    ----------
            64
    
    SQL> select n.name, s.value from v$mystat s, v$statname n where n.statistic# = s.statistic# and n.name in ('parse count (hard)', 'parse count (total)');
    
    NAME                      VALUE
    -------------------- ----------
    parse count (total)         526
    parse count (hard)          288
    
    SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'select /* test1 */%';
    
    SQL_TEXT                       EXECUTIONS PARSE_CALLS
    ------------------------------ ---------- -----------
    select /* test1 */ MY_SEQUENCE          3           3
    _SEQ.nextval from dual
    

    硬解析由一个登记,sql已经注册了3个解析,因此1个硬解析(将其放入共享池)和2个软解析。

    为什么软解析?为了使“无解析”发生​​,客户端代码必须保持语句句柄并重新执行它。 即如果我们用Java编写这个,我们会写这个:

        public static int getNextSeq(String str)
      throws Exception 
        {
            if (sel == null)
          {
            sel = con.prepareStatement("select MY_SEQUENCE_SEQ.nextval v from dual "+str);
          }
        ResultSet rs = sel.executeQuery();
        int seqVal=0;
        while (rs.next()) 
        {
          seqVal = rs.getInt("V");
        }
        return seqVal;
        }
    

    即。如果我们还没有这样做,我们只调用PrepareStatement。如果我们用

    执行此代码
    System.out.println(getNextSeq(args[0]));
    System.out.println(getNextSeq(args[0]));
    System.out.println(getNextSeq(args[0]));
    

    我们可以看到这一点:

    SQL> host java Prep two
    70
    71
    72
    
    SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'select %two';
    
    SQL_TEXT                       EXECUTIONS PARSE_CALLS
    ------------------------------ ---------- -----------
    select MY_SEQUENCE_SEQ.nextval          3           1
     v from dual two
    

    现在oracle DID不解析SQL与1硬解析的区别。如果Java代码写得不好,我们会看到:

    sel = con.prepareStatement("select MY_SEQUENCE_SEQ.nextval v from dual "+str);
    ResultSet rs = sel.executeQuery();
    
    
    SQL> host java Prep three
    73
    74
    75
    
    SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'select %three';
    
    SQL_TEXT                       EXECUTIONS PARSE_CALLS
    ------------------------------ ---------- -----------
    select MY_SEQUENCE_SEQ.nextval          3           3
     v from dual three
    

    现在我们看到解析count =执行。换句话说,我们软解析每个不理想的调用。再次不是Oracle限制,只是糟糕的客户端实现。

    使用PL / SQL,我们不必担心这个问题。为什么? PL / SQL不会解析它作为运行SQL的优化很多(毫无疑问!)。 例如:

    SQL> declare
      2    v_seq number;
      3  begin
      4    for idx in 1..3 loop
      5      select MY_SEQUENCE_SEQ.nextval into v_seq from dual pls_test;
      6    end loop;
      7  end;
      8  /
    
    PL/SQL procedure successfully completed.
    
    SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'SELECT %PLS_TEST';
    
    SQL_TEXT                       EXECUTIONS PARSE_CALLS
    ------------------------------ ---------- -----------
    SELECT MY_SEQUENCE_SEQ.NEXTVAL          3           1
     FROM DUAL PLS_TEST
    

    现在,pl / sql为我们做这个优化有一个警告,那就是参数SESSION_CACHED_CURSORS。在给定的会话中,Oracle将为我们打开一组游标(即它们是软打开的,即如果我们需要更多的游标,它将关闭它们)。因此,如果我们有SESSION_CACHED_CURSORS = 0并重复上述测试,我们将看到软解析突然陷入:

    SQL> alter session set session_cached_cursors=0;
    
    Session altered.
    
    SQL> declare
      2    v_seq number;
      3  begin
      4    for idx in 1..3 loop
      5      select MY_SEQUENCE_SEQ.nextval into v_seq from dual pls_test2;
      6    end loop;
      7  end;
      8  /
    
    PL/SQL procedure successfully completed.
    
    SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'SELECT %PLS_TEST2';
    
    SQL_TEXT                       EXECUTIONS PARSE_CALLS
    ------------------------------ ---------- -----------
    SELECT MY_SEQUENCE_SEQ.NEXTVAL          3           3
     FROM DUAL PLS_TEST2
    
    显然,缓存游标的值越高,我们就越需要避免软解析并达到避免完全解析的圣杯。

答案 1 :(得分:1)

这取决于您如何在客户端处理语句。如果你保留相同的变量/处理程序,则不应该对每个调用进行解析。

<小时/> 如果您在每次调用时创建并释放语句,您可以期望在共享池(软解析)或重新编译(硬解析)中搜索并找到sql。
此外,您的下划线平台可以缓存它的级别 - 以避免软解析。还有服务器参数文件来调整缓存的会话游标的大小。