2

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 Database 10g リリース 10.2.0.4.0 - 64 ビット製品

クライアント: ヒキガエル バージョン 11.5.1.2

4

2 に答える 2

4

これは Oracle の障害ではなく、TOAD が SQL を Oracle に送信する方法にすぎません。つまり、toad はステートメント ハンドルを oracle にキャッシュせず、完了時にクローズするだけです。

Oracle は、クエリが SQL エンジンに送信されるときに、クエリに対して 3 つの主な処理のいずれかを行います。

  1. ハードパース
  2. ソフトパース
  3. 解析しない

つまり、ケース 3 になりたいのですが、ケース 1 にはなりたくありません。では、それぞれのケースはいつ発生するのでしょうか。

SQL が共有プールにまったくない場合、または SQL が共有プールにあるが、使用中のバインド変数/リテラル​​が現在の SQL を使用できないことを意味する場合、ハード解析が発生します。たとえば、この SQL を 3 回発行したとします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

ハード解析が 1 つ増え、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

現在、オラクルは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 がこの最適化を行う際の注意事項が 1 つあります。それは、パラメータ 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

明らかに、キャッシュされたカーソルの値が高いほど、ソフト解析を回避し、完全な解析を回避するという聖杯に到達する必要がある可能性が高くなります。

于 2012-12-11T22:01:02.507 に答える
1

クライアント側でステートメントをどのように処理するかによって異なります。同じ変数/ハンドラーを保持する場合、各呼び出しで解析する必要はありません。


呼び出しごとにステートメントを作成して解放すると、SQL が検索されて共有プールで見つかる (ソフト解析) か、再コンパイルされる (ハード解析) ことが期待できます。
また、下線を引くプラットフォームはそのレベルでキャッシュできます - ソフト解析を避けるためです。また、キャッシュされたセッション カーソルのサイズを設定するサーバー パラメータ ファイルもあります。

于 2012-12-11T19:23:26.003 に答える