【问题标题】:How oracle parsers deal with sequencesoracle 解析器如何处理序列
【发布时间】:2012-12-11 18:17:59
【问题描述】:

为什么 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. 从第 2 点开始执行查询。

得到的结果:

EXECUTIONS  - n
PARSE_CALSS - n

测试于:

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

客户端:Toad 版本 11.5.1.2

【问题讨论】:

  • 这对我来说行不通。 create sequence my_sequencer;,然后多次运行 SELECT MY_SEQUENCER.nextval FROM dual;,在 select executions, parse_calls, v$sql.* from v$sql where lower(sql_text) like '%my_sequencer%'; 中,执行次数增加,但 parse_calls 保持在较低水平。您可能需要为我们创建一个完全可重现的测试用例来调试问题。
  • 我的意思是你能提供序列的 DDL,以及获取 Executions 和 Parse_calls 的 SQL,以及你如何调用 SELECT 吗?现在我无法重现您的问题。它可能取决于 SELECT 的上下文 - 它是从 PL/SQL 块、动态 SQL 等调用的。
  • @jonearles,我已经手动完成了测试,但它解决了与解析 quencer 相同的结果。一些新的想法?
  • @jonearles,经过一些分析后,我注意到这是非常常见的行为。所以我的问题是你是怎么测试的,所以你得到了不同的结果?
  • select value from v$parameter where name = 'session_cached_cursors'; 是否设置为 0?

标签: oracle sequence


【解决方案1】:

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

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

  1. 硬解析
  2. 软解析
  3. 不解析它

即,我们想要在第 3 种情况下,我们当然不想在第 1 种情况下! 那么每个案例什么时候会发生呢?

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

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

hard parses 增加了 1 个,sql 已经注册了 3 个 parses,所以 1 个 hard parses(放入共享池)和 2 个 soft parses。

为什么要软解析?为了“不解析”发生​​,客户端代码必须保持语句句柄并重新执行它。 也就是说,如果我们用 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 除了第 1 次硬解析之外,还没有解析 SQL。如果 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

现在我们看到解析计数 = 执行次数。换句话说,我们正在软解析每个不理想的调用。再次不是 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

显然,缓存游标的值越高,我们就越需要避免软解析并达到避免一起解析的圣杯。

【讨论】:

  • 不错的答案。但值得注意的是,对于/* test 1*/,在 11.2.0.3 上,EXECUTIONS = PARSE_CALLS 仅在第 3 次执行之前。之后,不再使用 SQL*Plus 或 Toad 等其他客户端解析该语句。
【解决方案2】:

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


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

【讨论】:

  • Ad.1 查询中没有变量。 Ad.2 即使选择是批量发送的,解析也会增加。广告3。这与问题没有任何关系。
猜你喜欢
  • 2021-05-12
  • 1970-01-01
  • 2019-04-29
  • 1970-01-01
  • 1970-01-01
  • 2018-03-18
  • 2017-10-09
  • 1970-01-01
  • 2021-07-03
相关资源
最近更新 更多