Oracle中SQL解析的流程

Oracle中SQL解析的主要流程:
sql_parse_digram

我们说的游标概念比较复杂,它可以是客户端程序中的游标,服务进程中的私有游标,以及服务器端共享池里的共享游标。假设一个游标被打开了,一般来说它的共享游标信息(包括执行计划,优化树等)总是会在SQL AREA里,无需再次软/硬解析。

SESSION_CACHED_CURSORS是Oracle中的一个初始化参数(修改必须重启实例),指定了每个会话缓存的游标上限(保留在PGA中);客户端程序中open cursor的要求仍会被传递给服务进程,服务进程首先扫描自身缓存的游标信息,如果命中则可以避免软解析,也有人称它为“软软解析”。

HOLD_CURSOR是预编译程序中的一个参数,它指定了私有游标是否因该被缓存,这里不做展开。

在分析工具tkprof中hard parse与soft parse被同等对待,都被认为是parse;软解析即会造成parse总数上升。

软解析避免了硬解析过程中的几个步骤,但仍包括了初始化的语法,语义解析并计算语句HASH值与SQL AREA中已有语句进行对比;若匹配则查询优化等昂贵的操作得以避免。

另请注意,10053事件仅在硬解析过程中被触发。

  1. What are the Oracle Precompiler options HOLD_CURSOR and RELEASE_CURSOR
    and how do they affect the performance of a PCC program?

    First of all, these options apply to implicit and explicit cursors in
    all precompiled languages except Pro*Ada. They apply ONLY to implicit
    cursors associated with INSERT, UPDATE, DELETE, or a single row SELECT
    in Pro*Ada. An explicit cursor in a program is one explicitly created
    with EXEC SQL DECLARE C1 CURSOR FOR…

    This bulletin discusses what these options do internally and how changing
    them affects program performance.

    HOLD_CURSOR is a precompiler command line parameter.

    Summary Operation:
    ~~~~~~~~~~~~~~~~~~

    Statement submitted
    |
    Is it in an open cursor?–YES–> EXECUTE < ---.
    |NO ^ ^ |
    Is HOLD_CURSOR=Y & | | |
    stmt in HC 'cache'?-----YES------' | |
    |NO | |
    Hash it + compare with sql area | |
    | | |
    is it in sql area -----YES----(Soft Parse) |
    |NO |
    --------------(Hard Parse)--------------------'

    NB: Both the soft and hard parse register as a parse in tkprof.
    If the cursor is open and it is not in the SQL_AREA then it clearly
    has to parse it (not shown in the diagram!)

    The HOLD_CURSOR and RELEASE_CURSOR Options
    ------------------------------------------

    What are the Oracle Precompiler options HOLD_CURSOR and RELEASE_CURSOR
    and how do they affect the performance of a PCC program?

    First of all, these options apply to implicit and explicit cursors in
    all precompiled languages except Pro*Ada. They apply ONLY to implicit
    cursors associated with INSERT, UPDATE, DELETE, or a single row SELECT
    in Pro*Ada. An explicit cursor in a program is one explicitly created
    with EXEC SQL DECLARE C1 CURSOR FOR...

    Following will be a discussion of what these options do internally and
    how changing them affects program performance.

    What exactly do we mean by CURSOR? Unfortunately, we mean two differ-
    ent things:

    1. The program cursor - A data structure associated with a SQL
    statement.

    A program cursor is declared for each SQL statement that the
    precompiler finds in your program. For the statements

    EXEC SQL DECLARE SEL_EMP_CURS CURSOR FOR...
    EXEC SQL INSERT...

    PCC will declare two program cursors, say c1 and c2.

    2. The Oracle cursor (also called the context area) - The work
    area created dynamically at run time; this area contains the
    parsed statement, the addresses of the host variables, and
    other information necessary to execute the SQL statement.

    These two cursors are linked together via the cursor cache. The ini-
    tial size of the cursor cache is determined by the MAXOPENCURSORS op-
    tion.

    The following diagram illustrates the relationship described above af-
    ter an insert and an update have been executed in your program:

    CURSOR CACHE
    +-----------------+
    EXEC SQL INSERT... | Cache entry | Oracle
    Program cursor P(1) <----> | C(1) | < ----> cursor
    +—————–+
    EXEC SQL UPDATE… | Cache entry | Oracle
    Program cursor P(2) < ----> | C(2) | < ----> cursor
    +—————–+
    . . .
    . . .
    +—————–+
    EXEC SQL DELETE… | Cache entry |
    Pgm cursor P(MAXOPENCURSORS) |P(MAXOPENCURSORS)|
    +—————–+
    EXEC SQL SELECT…
    Pgm cursor P(MAXOPENCURSORS+1)

    etc…

    How are the HOLD_CURSOR and RELEASE_CURSOR options related to this
    view?

    The HOLD_CURSOR option deals with the link between the program cursor
    and its cache entry.

    The RELEASE_CURSOR option deals with the link between the Oracle cur-
    sor and the cache entry.

    For SQL statements that are FREQUENTLY RE-EXECUTED, the bottom line is
    this: if you want to maximize performance, make sure these SQL state-
    ments stay “glued” to their respective Oracle cursor.

    What does it mean when a SQL statement is “glued” to its Oracle cur-
    sor? It means that both links between the SQL statement and its Ora-
    cle cursor are made permanent.

    Why would you want to keep a statement “glued” to its context area?
    Because the context area contains the parsed statement and other in-
    formation necessary to execute the statement, such as the addresses of
    the host variables. Maintaining access to this information makes sub-
    sequent execution of the statement much faster.

    How do you “glue” a statement to a cache entry? By correct use of the
    HOLD_CURSOR and RELEASE_CURSOR options via the PCC command line or in-
    line with EXEC ORACLE OPTION(…).

    For instance, with HOLD_CURSOR=YES as the Oracle option, a cache entry
    cannot be flagged for reuse. This has important implications. If all
    cache entries have been used up and a new cache entry is needed for a
    new SQL statement such that the number of cache entries would now ex-
    ceed the number specified in MAXOPENCURSORS, Oracle will use the first
    cache entry marked reuseable.

    For example, in the above diagram, if the cache entry C(1) is marked
    reusable, and the program is about to execute the EXEC SQL SELECT…
    (program cursor P(MAXOPENCURSORS+1), and the number of cache entries
    in use already equals MAXOPENCURSORS, cache entry C(1) and its Oracle
    cursor will now be linked to the select statement. A subsequent exe-
    cution of the insert statement would require pre-empting a cache entry
    and its Oracle cursor from another SQL statement and performing a re-
    parse.

    Correspondingly, with the default RELEASE_CURSOR=NO as the Oracle op-
    tion, the link between the cache entry and the Oracle cursor (the con-
    text area) is maintained after the statement is executed so that the
    parsed statement and, more importantly, the allocated memory stay
    available.

    The freeing up of this memory by RELEASE_CURSOR=YES means that the
    next statement that gets linked to this cache entry will require an
    expensive reallocation of memory in addition to a reparse. Ugh! Why
    would anybody want RELEASE_CURSOR=YES? We will see later on.

    Program cursor – – – – – [ Cursor cache entry ] – – – – – Oracle
    for SQL statement cursor
    ^ ^
    HOLD_CURSOR=YES RELEASE_CURSOR=NO
    program cursor is permanently cache entry maintains the
    linked to its cache entry. address of its context area.

    So the HOLD_CURSOR option is intimately tied to the MAXOPENCURSORS op-
    tion. What exactly is the MAXOPENCURSORS option? First of all, MAX-
    OPENCURSORS is a misnomer. It should more appropriately be called
    INITIAL_CURSOR_CACHE_SIZE. (Okay, so it’s a mouthful.) Anyway, if
    all cursor cache entries are currently marked “not reusable” either
    because of the HOLD_CURSOR option or because the associated statement
    is currently being executed (an explicitly opened cursor is still be-
    ing fetched on and hasn’t been closed yet), then a request for a new
    cursor will actually result in the extension of the cursor cache at
    runtime (i.e. if MAXOPENCURSORS=10, and all 10 entries are active,
    then an 11th will be created). Just letting the precompiler reuse the
    oldest cache entry won’t always work, as the following example illus-
    trates: Imagine the case where the user has ten explicitly declared
    cursors opened, and wants to execute an eleventh. If the program ac-
    tually reuses the oldest program cursor, the user would lose his cur-
    rent position in the first cursor and would not be able to fetch from
    it anymore.

    By the way, if an eleventh cache entry is created, when that cursor is
    closed the eleventh entry is not removed. Setting MAXOPENCURSORS low
    saves memory, but causes potentially expensive dynamic allocations of
    new cache entries if they’re needed. Setting it high assures quick
    execution, but may use more memory than necessary.

    What if a statement is not executed repeatedly in a program? Then you
    could go with the other options HOLD_CURSOR=NO and RELEASE_CURSOR=YES.
    With the HOLD_CURSOR=NO option, the link between a program cursor and
    its cache entry is not permanent. The cache entry is automatically
    marked reusable in case it is needed. With the RELEASE_CURSOR=YES op-
    tion, the Oracle cursor (the context area) is automatically freed and
    the parsed statement lost. A reason you might use this option is if
    you are limited by the number of Oracle cursors (MAXOPENCURSORS) at
    your site due to memory issues. You may want to incur the cost of re-
    allocating memory and reparsing in order to manage memory more effec-
    tively.

    An advantage of setting RELEASE_CURSOR=YES is that until the link be-
    tween the cache entry and the Oracle cursor (context area) is removed,
    ORACLE keeps parse locks on any tables referenced in the SQL state-
    ment. These parse locks prevent other users and you from ALTERing or
    DROPping the tables (does ORA-0057 sound familiar?). Also, in Version
    5, it will free up the read-consistent image of the referenced tables
    stored in ORACLE’s Before Image file.

    What do we mean when we say that RELEASE_CURSOR=YES takes precedence
    over HOLD_CURSOR=YES? With RELEASE_CURSOR=YES, the link between the
    Oracle cursor and the cache entry is cut and the Oracle cursor is
    freed (closed), so even if your program cursor is permanently linked
    to the cache entry because HOLD_CURSOR=YES, you will still have to re-
    allocate memory and reparse the statement. So subsequent executions
    of a statement don’t benefit from the HOLD_CURSOR=YES option because
    RELEASE_CURSOR=YES.

    For programmers experienced with OCI, here’s the OCI equivalent of
    what’s happening:

    #define MAXOPENCURSORS 5

    char *sql_stmts[10];
    curs_def cursor[MAXOPENCURSORS];

    oopen(cursor[0],…);
    osql3(cursor[0],…,sql_stmts[0],…);

    An example of a “cache entry” being linked to another SQL statement
    later on in the program is as follows:

    osql3(cursor[0],…,sql_stmts[5],…);

    I am forced to reuse one of my “cache entries” to execute the sixth
    SQL statement.

    An example of a context area being freed is:

    oclose(cursor[0]);

    Reusing cursor[0] would require another oopen() and another osql3()–
    another dynamic allocation of memory and another reparse.

    Conclusion
    ———-

    As a programmer, you will get the most from these options by using
    them selectively inline rather than specifying them as options at pre-
    compile time.

  2. SCRIPT – to Gauge the Impact of the SESSION_CACHED_CURSORS Parameter
    select
    to_char(100 * sess / calls, ‘999999999990.00’) || ‘%’ cursor_cache_hits,
    to_char(100 * (calls – sess – hard) / calls, ‘999990.00’) || ‘%’ soft_parses,
    to_char(100 * hard / calls, ‘999990.00’) || ‘%’ hard_parses
    from
    ( select value calls from v$sysstat where name = ‘parse count (total)’ ),
    ( select value hard from v$sysstat where name = ‘parse count (hard)’ ),
    ( select value sess from v$sysstat where name = ‘session cursor cache hits’ )
    /

    SCRIPT – to Tune the ‘SESSION_CACHED_CURSORS’ and ‘OPEN_CURSORS’ Parameters
    select
    ‘session_cached_cursors’ parameter,
    lpad(value, 5) value,
    decode(value, 0, ‘ n/a’, to_char(100 * used / value, ‘990’) || ‘%’) usage
    from
    ( select
    max(s.value) used
    from
    v$statname n,
    v$sesstat s
    where
    n.name = ‘session cursor cache count’ and
    s.statistic# = n.statistic#
    ),
    ( select
    value
    from
    v$parameter
    where
    name = ‘session_cached_cursors’
    )
    union all
    select
    ‘open_cursors’,
    lpad(value, 5),
    to_char(100 * used / value, ‘990’) || ‘%’
    from
    ( select
    max(sum(s.value)) used
    from
    v$statname n,
    v$sesstat s
    where
    n.name in (‘opened cursors current’, ‘session cursor cache count’) and
    s.statistic# = n.statistic#
    group by
    s.sid
    ),
    ( select
    value
    from
    v$parameter
    where
    name = ‘open_cursors’
    )
    /

    Sample Output
    PARAMETER VALUE USAGE
    ———————- ——– ———
    session_cached_cursors 0 n/a
    open_cursors 300 1%

  3. 指定了每个会话缓存的游标上限(保留在PGA中)
    ——————————————————–
    SESSION_CACHED_CURSOR主要还是cache在SGA里面吧,PGA也会用到一些

    • 你好,

      游标缓存本身是保存在sga的shared pool共享池中的, 但是SESSION_CACHED_CURSOR指定的是session会话缓存游标的上限,主要体现在PGA的UGA中保存这些缓存游标的地址,如下文:

      SQL> oradebug setmypid ;
      Statement processed.
      SQL> oradebug dump errorstack 4;
      Statement processed.

      trace content

      —– Session Open Cursors —–

      —– Session Cached Cursor Dump —–
      —– Generic Session Cached Cursor Dump —–
      ———————————————————–
      ————– Generic Session Cached Cursors Dump ——–
      ———————————————————–
      hash table=0x7f9baed440f0 cnt=4 LRU=0x7f9baed36ca0 cnt=4 hit=3 max=50 NumberOfTypes=6
      type#0 name=DICTION count=0
      type#1 name=BUNDLE count=0
      type#2 name=SESSION count=4
      type#3 name=PL/SQL count=0
      type#4 name=CONSTRA count=0
      type#5 name=REPLICA count=0
      Bucket#051 seg=0x7f9baed44a78 nit=5 nal=5 ips=5 sz=56 flg=3 ucnt=1
      0 cob=0x7f9baed54940 idx=33 flg=0 typ=2 cur=0x7f9baed58308 lru=1 fl=1
      Bucket#098 seg=0x7f9baed45348 nit=5 nal=5 ips=5 sz=56 flg=3 ucnt=1
      0 cob=0x7f9baed5c2b0 idx=62 flg=0 typ=2 cur=0x7f9baed5c3e0 lru=1 fl=1
      Bucket#123 seg=0x7f9baed457f8 nit=5 nal=5 ips=5 sz=56 flg=3 ucnt=1
      0 cob=0x7f9baed5be18 idx=7b flg=0 typ=2 cur=0x7f9baed5bf48 lru=1 fl=1
      Bucket#203 seg=0x7f9baed466f8 nit=5 nal=5 ips=5 sz=56 flg=3 ucnt=1
      0 cob=0x7f9baed5b980 idx=cb flg=0 typ=2 cur=0x7f9baed5bab0 lru=1 fl=1
      ———————————————————–
      ——Finished Dumping Generic Session Cached Cursors——
      ———————————————————–

      在dedicated server模式下这些”Session Cached Cursors”在PGA的UGA中, 而在shared server模式下则UGA中SGA中。

      当SESSION_CACHED_CURSOR参数设置不当,且session数过多的场景中可能导致UGA消耗大量的内存。

      希望对你有所帮助,谢谢!

  4. 提交语句后,是否存在打开的游标?
    是否SESSION_CACHED_CURSORS>0 且游标在SESVER断的会话游标缓存中?

    上述这两步,ORACLE是如何作判断的?

    • 一个open cursor 本质上就是一个kgllk breakable library cache lock、 session cached cursors是缓存在PGA中的 shared cursor address列表 这个可以通过errorstack level 3 trace看到,所以找上述2样东西很容易。

      • ORACLE是如何确认提交的语句,已经存在PGA中(是否存在打开的游标)?它作了哪些比较?
        此时ORACLE是否检查了语句的语法和语义?

      • ORACLE是如何确认提交的语句,已经存在PGA中(是否存在打开的游标)?它作了哪些比较?
        此时ORACLE是否检查了该语句的语法和语义?