SQL計画管理
一番とっつき易そうなところからやってみる。
SQL計画管理とは
SQL計画の管理を使用すると、SQL計画の情報を取得、選択および改良するためのコンポーネントが用意されているため、SQL文の実行計画に対する突然の変更によるパフォーマンスの低下を回避できます。
15 SQL計画の管理の使用方法
シナリオ
- フルスキャンの計画履歴が作成される
- インデックスを作成
- インデックススキャンの計画履歴が作成される(最初は未受入)
- フルスキャンの計画履歴が使われる
- 計画履歴をEVOLVE(受入)
- インデックススキャンの計画履歴が使われるようになる
- インデックススキャンの計画履歴を無効化
- フルスキャンの計画履歴が使われる
実験対象のテーブルとデータ
SQL> desc person 名前 NULL? 型 ----------------------------------------- -------- ---------------------------- ID NUMBER(38) NAME VARCHAR2(20) SQL> select * from person 2 ; ID NAME ---------- -------------------- 1 hoge 2 moge 3 toge SQL>
SQL計画管理の使用が有効、SQL計画履歴の取得が有効
SQL> select name, value from v$parameter 2 where name IN ('optimizer_use_sql_plan_baselines', 'optimizer_capture_sql_p lan_baselines'); NAME -------------------------------------------------------------------------------- VALUE -------------------------------------------------------------------------------- optimizer_capture_sql_plan_baselines TRUE optimizer_use_sql_plan_baselines TRUE SQL>
初期化パラメータ名 | 説明 |
---|---|
optimizer_use_sql_plan_baselines | OPTIMIZER_USE_SQL_PLAN_BASELINESでは、SQL Management Baseに保存されているSQLプラン・ベースラインを使用可能または使用禁止にします。使用可能な場合は、オプティマイザはコンパイル対象のSQL文の SQLプラン・ベースラインを検索します。SQLプラン・ベースラインがSQL Management Baseにある場合、オプティマイザは各ベースラインのプランを見積もり、コストが最も低いプランを取り出します。 |
optimizer_capture_sql_plan_baselines | OPTIMIZER_CAPTURE_SQL_PLAN_BASELINESでは、反復可能なSQL文の自動認識、およびそのような文のSQLプラン・ベースラインの生成を使用可能または使用禁止にします。 |
SQL計画履歴が無い状態
SQL> select count(*) from dba_sql_plan_baselines; COUNT(*) ---------- 0 SQL>
最初はSQL計画履歴が空の状態である。
SQLを実行(1回目)
SQL> select /*+ AAA */ * from person where id = 1; ID NAME ---------- -------------------- 1 hoge
SQL> select sql_text, sql_handle, plan_name, origin, enabled, accepted, fixed fr om dba_sql_plan_baselines where sql_text LIKE '%AAA%'; レコードが選択されませんでした。 SQL>
SQLを実行(2回目)
SQL> select /*+ AAA */ * from person where id = 1; ID NAME ---------- -------------------- 1 hoge
SQLが2回目実行されると、SQL計画履歴(plan_name=SYS_SQL_PLAN_d4bda9c08f0075a4)に入る。
SQL> select sql_text, sql_handle, plan_name, origin, enabled, accepted, fixed fr om dba_sql_plan_baselines where sql_text LIKE '%AAA%'; SQL_TEXT -------------------------------------------------------------------------------- SQL_HANDLE PLAN_NAME ORIGIN ENA ------------------------------ ------------------------------ -------------- --- ACC FIX --- --- select /*+ AAA */ * from person where id = 1 SYS_SQL_39d8a16ad4bda9c0 SYS_SQL_PLAN_d4bda9c08f0075a4 AUTO-CAPTURE YES YES NO SQL>
enabled=yes、かつ、accepted=yesなので、以降この計画が使われる。
インデックスを作成する
SQL> create index idx_person on person (id); 索引が作成されました。 SQL>
ここで同じSQLを実行してても、実行計画が変わるように、おもむろにインデックスを張ってみる。
SQLを実行(3回目)
SQL> select /*+ AAA */ * from person where id = 1; ID NAME ---------- -------------------- 1 hoge
新しい実行計画となったので、計画履歴(plan_name=SYS_SQL_PLAN_d4bda9c0f97a4e5d)が作成される。
(この場合は2回実行しなくてもよいらしい。SQL_HANDLEとしては既に登録されているから?)
ただし、後から作られた方はaccepted=NOになっているので、使われない。
SQL> select sql_text, sql_handle, plan_name, origin, enabled, accepted, fixed fr om dba_sql_plan_baselines where sql_text LIKE '% AAA %'; SQL_TEXT -------------------------------------------------------------------------------- SQL_HANDLE PLAN_NAME ORIGIN ENA ------------------------------ ------------------------------ -------------- --- ACC FIX --- --- select /*+ AAA */ * from person where id = 1 SYS_SQL_39d8a16ad4bda9c0 SYS_SQL_PLAN_d4bda9c08f0075a4 AUTO-CAPTURE YES YES NO select /*+ AAA */ * from person where id = 1 SYS_SQL_39d8a16ad4bda9c0 SYS_SQL_PLAN_d4bda9c0f97a4e5d AUTO-CAPTURE YES NO NO SQL_TEXT -------------------------------------------------------------------------------- SQL_HANDLE PLAN_NAME ORIGIN ENA ------------------------------ ------------------------------ -------------- --- ACC FIX --- --- SQL>
計画を表示してみる(最初に作られた方)
SQL> select * from table(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE('SYS_SQL_39d8a16ad 4bda9c0', 'SYS_SQL_PLAN_d4bda9c08f0075a4')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- SQL handle: SYS_SQL_39d8a16ad4bda9c0 SQL text: select /*+ AAA */ * from person where id = 1 -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- Plan name: SYS_SQL_PLAN_d4bda9c08f0075a4 Enabled: YES Fixed: NO Accepted: YES Origin: AUTO-CAPTURE -------------------------------------------------------------------------------- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 1493655343 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 100 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| PERSON | 4 | 100 | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- 1 - filter("ID"=1) 24行が選択されました。 SQL>
まだインデックスが無かったころの実行計画なので、フルスキャンになってます。
計画を表示してみる(次に作られた方)
SQL> select * from table(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE('SYS_SQL_39d8a16ad 4bda9c0', 'SYS_SQL_PLAN_d4bda9c0f97a4e5d')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- SQL handle: SYS_SQL_39d8a16ad4bda9c0 SQL text: select /*+ AAA */ * from person where id = 1 -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- Plan name: SYS_SQL_PLAN_d4bda9c0f97a4e5d Enabled: YES Fixed: NO Accepted: NO Origin: AUTO-CAPTURE -------------------------------------------------------------------------------- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 1414301407 -------------------------------------------------------------------------------- ---------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- ---------- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 100 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| PERSON | 4 | 100 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_PERSON | 2 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------- ---------- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=1) 25行が選択されました。 SQL>
インデックスが作られた後なので、インデックスが使われて、インデックススキャンになってます。
ちなみに今使われている計画は
SQL> set autotrace on explain SQL> select /*+ AAA */ * from person where id = 1; ID NAME ---------- -------------------- 1 hoge 実行計画 ---------------------------------------------------------- Plan hash value: 1493655343 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 100 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| PERSON | 4 | 100 | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID"=1) Note ----- - SQL plan baseline "SYS_SQL_PLAN_d4bda9c08f0075a4" used for this statement SQL>
インデックススキャンの方の計画履歴はaccepted=NOなので、まだ使われません。
計画履歴をEVOLVEする
SQL> exec MY_EVOLVE_SQL_PLAN_BASELINE('SYS_SQL_39d8a16ad4bda9c0'); PL/SQLプロシージャが正常に完了しました。 SQL>
計画履歴を表示する
SQL> select sql_text, sql_handle, plan_name, origin, enabled, accepted, fixed fr om dba_sql_plan_baselines where sql_handle = 'SYS_SQL_39d8a16ad4bda9c0'; SQL_TEXT -------------------------------------------------------------------------------- SQL_HANDLE PLAN_NAME ORIGIN ENA ------------------------------ ------------------------------ -------------- --- ACC FIX --- --- select /*+ AAA */ * from person where id = 1 SYS_SQL_39d8a16ad4bda9c0 SYS_SQL_PLAN_d4bda9c08f0075a4 AUTO-CAPTURE YES YES NO select /*+ AAA */ * from person where id = 1 SYS_SQL_39d8a16ad4bda9c0 SYS_SQL_PLAN_d4bda9c0f97a4e5d AUTO-CAPTURE YES YES NO SQL_TEXT -------------------------------------------------------------------------------- SQL_HANDLE PLAN_NAME ORIGIN ENA ------------------------------ ------------------------------ -------------- --- ACC FIX --- --- SQL>
インデックススキャンの方の計画履歴(plan_name=SYS_SQL_PLAN_d4bda9c0f97a4e5d)のacceptedがYESになりました。
これで、こちらの計画が使われるはずです。
ちなみに今使われている計画は
SQL> set autotrace on explain SQL> select /*+ AAA */ * from person where id = 1; ID NAME ---------- -------------------- 1 hoge 実行計画 ---------------------------------------------------------- Plan hash value: 1414301407 -------------------------------------------------------------------------------- ---------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- ---------- | 0 | SELECT STATEMENT | | 1 | 25 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| PERSON | 1 | 25 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_PERSON | 1 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------- ---------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=1) Note ----- - dynamic sampling used for this statement - SQL plan baseline "SYS_SQL_PLAN_d4bda9c0f97a4e5d" used for this statement SQL>
想定通り、インデックススキャンになりました。
EVOLVEした計画履歴をDISABLEにしてみる
SQL> exec my_alter_sql_plan_baseline('SYS_SQL_39d8a16ad4bda9c0', 'SYS_SQL_PLAN_d 4bda9c0f97a4e5d', 'enabled', 'no'); PL/SQLプロシージャが正常に完了しました。 SQL>
MY_ALTER_SQL_PLAN_BASELINEの定義(SQL*Plusから以下略)
CREATE OR REPLACE PROCEDURE MY_ALTER_SQL_PLAN_BASELINE(in_sql_handle VARCHAR2, in_plan_name VARCHAR2, in_attribute_name VARCHAR2, in_attribute_value VARCHAR2) AUTHID CURRENT_USER IS rtn PLS_INTEGER; BEGIN rtn := DBMS_SPM.ALTER_SQL_PLAN_BASELINE( sql_handle => in_sql_handle , plan_name => in_plan_name , attribute_name => in_attribute_name , attribute_value => in_attribute_value); END; /
計画履歴を表示する
SQL> select sql_text, sql_handle, plan_name, origin, enabled, accepted, fixed fr om dba_sql_plan_baselines where sql_handle = 'SYS_SQL_39d8a16ad4bda9c0'; SQL_TEXT -------------------------------------------------------------------------------- SQL_HANDLE PLAN_NAME ORIGIN ENA ------------------------------ ------------------------------ -------------- --- ACC FIX --- --- select /*+ AAA */ * from person where id = 1 SYS_SQL_39d8a16ad4bda9c0 SYS_SQL_PLAN_d4bda9c08f0075a4 AUTO-CAPTURE YES YES NO select /*+ AAA */ * from person where id = 1 SYS_SQL_39d8a16ad4bda9c0 SYS_SQL_PLAN_d4bda9c0f97a4e5d AUTO-CAPTURE NO YES NO SQL_TEXT -------------------------------------------------------------------------------- SQL_HANDLE PLAN_NAME ORIGIN ENA ------------------------------ ------------------------------ -------------- --- ACC FIX --- --- SQL>
インデックススキャンの方の計画履歴(plan_name=SYS_SQL_PLAN_d4bda9c0f97a4e5d)のenabledがNOになりました。
これで、こちらの計画は使われないはずです。
ちなみに今使われている計画は
SQL> set autotrace on explain SQL> select /*+ AAA */ * from person where id = 1; ID NAME ---------- -------------------- 1 hoge 実行計画 ---------------------------------------------------------- Plan hash value: 1493655343 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 100 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| PERSON | 4 | 100 | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID"=1) Note ----- - SQL plan baseline "SYS_SQL_PLAN_d4bda9c08f0075a4" used for this statement SQL>
想定通り、フルスキャンになりました。