しばちょう先生の試して納得!DBAへの道 indexページ▶▶

 


みなさん、こんにちは。”しばちょう”こと柴田長(しばた つかさ)です。3月はお客様のシステムのデータベースにおいてSQLの性能テストに没頭していました。いくつものSQLトレースの結果を比較する毎日はあっという間に過ぎるもので、改めてデータベースが本当に好きなのだなと感じさせられました。それ以外の記憶がないのですから。

img_skillup_shibacho_120329_01.jpg

さて、第5回目の今回は、いよいよSQLの実行計画を見てみることにしましょう。まずは基本的な実行計画の取得方法を学び、実行計画を使用して性能差に納得する流れを体験して頂きたいと思います。「SQLが速い/遅い」という事実だけで終わりではなく、「どんな理由でSQLが速い/遅い」まで分析できることがDBAに求められることですから、是非ともチャレンジしてみてください。

以下の演習をこれまでの演習で使用したOracle Database 11g Release 2 Enterprise Editionのデータベースで試してみてください。

 

■演習0.演習前の環境準備(第4回の演習終了後のデータベースの状態を今回の環境へ変更します)

sqlplus TRY/TRY

SQL> 
drop table TBL3 purge;
create table TBL3 as select * from TBL2;

 TBL3表を作成し直します。TBL2表が存在していない方は、第4回の演習1~2を実行後に上記のSQLを実行してください。初めて演習される方は、第4回の演習0~2を実行後に上記のSQLを実行してください。

■演習1.SYSユーザーでバッファ・キャッシュ上のデータをフラッシュして下さい。

sqlplus / as sysdba
SQL> 
alter system FLUSH BUFFER_CACHE;

 実行計画とは全く関係ありませんが、一般的にSQLの性能テストを実施する前にはバッファ・キャッシュ上にキャッシュされたデータをフラッシュ(クリアとかクリーンアップとも言います)しておきます。バッファ・キャッシュ上にデータがキャッシュされている場合、SQL実行時にハードディスクへ読みに行く必要が無いので高速なレスポンスになる傾向があります。SQLの性能テストは繰り返しチューニングを行うので、SQL実行の度にバッファ・キャッシュの状態が変化していてはチューニング効果を正確に把握することが不可能ですから、バッファ・キャッシュの状態を統一化する意味があります。

例えば、インスタンス起動直後(バッファ・キャッシュ上に何もキャッシュされていない状態)にチューニング前の性能測定を行い、その後、特にバッファ・キャッシュをフラッシュせず(チューニング前の性能測定でキャッシュされたデータが残っている状態)にチューニング後の性能測定を行ったにも関わらず、「チューニングの結果、SQLが高速化した」と言っているケースが稀にあります。

もちろん、SQLがアクセスするデータがバッファ・キャッシュ上に100%キャッシュされている条件での性能テストであれば話が違いますので、どのような前提条件での性能テストであるのかを明確にしておきましょう。

ちなみに、「バッファ・キャッシュ上のデータが本当にフラッシュされたのか」と思いませんか?そんな鋭い方の為にバッファ・キャッシュ内を除くSELECT文を用意してみたので、是非試してみてくださいね。

sqlplus / as sysdba
SQL> 
col OBJECT_NAME for a36
set pagesize 50000
set linesize 150
select
  DO.OWNER, DO.OBJECT_NAME, BH.STATUS, count(*) BLOCKS
from V$BH BH, DBA_OBJECTS DO
where BH.OBJD = DO.DATA_OBJECT_ID
  and BH.TS# > 0
  and BH.STATUS != 'free'
group by DO.OWNER, DO.OBJECT_NAME, BH.STATUS
order by 1, 2, 3;

■演習2.OracleデータベースにおいてOSのファイル・システム・キャッシュ(ページ・キャッシュ)が使用されないように初期化パラメータ「FILESYSTEMIO_OPTIONS」を適切に設定してください。

sqlplus / as sysdba
SQL> show parameter filesystemio

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
filesystemio_options                 string      none

SQL> alter system set FILESYSTEMIO_OPTIONS='SETALL' scope=spfile;

SQL> shutdown immediate;
SQL> startup
SQL> show parameter filesystemio

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
filesystemio_options                 string      setall

 はい、初期化パラメータの変更なので、それほど難しくは無いかと思います。ただし、このパラメータに設定可能な文字列が何を意味するのかがリファレンス・マニュアルに記載されていないので、少し解説しておきます。

SQLの性能テストを実施する上で、排除しておきたいのがOSのファイル・システム・キャッシュの影響です。演習1のようにバッファ・キャッシュをフラッシュしても、Oracleのデータ・ファイルがOSのファイル・システム・キャッシュにキャッシュされている場合、サーバー・プロセスがデータ・ブロックを読みに行く先はハードディスクではなく物理メモリになります。データ・ファイルがOSのファイル・システム・キャッシュにキャッシュされているか否かによって、SQLの実行時間にブレが生じてしまうので好ましくないのは理解できるかと思います。この影響を排除するには、OSのファイル・システム・キャッシュから読み込まない、直接ハードディスクからデータを読み取るダイレクトI/Oになります。

FILESYSTEMIO_OPTIONSは、ファイル・システム上に配置しているデータ・ファイルに対するI/Oの設定を行なうパラメータであり、ファイル・システムに対する非同期I/O やダイレクトI/Oの有効化/無効化を制御可能です。

=====================================================
 none ファイル・システムに対する非同期I/OとダイレクトI/Oを無効化
 setall ファイル・システムに対する非同期I/OとダイレクトI/Oを有効化
 directIO ファイル・システムに対するダイレクトI/Oを有効化(非同期I/Oは無効)
 asynch ファイル・システムに対する非同期I/Oを有効化(ダイレクトI/Oは無効)
※ OSの種類やバージョンによってデフォルト設定が異なります。また、有効化不可能な設定値もあります。
=====================================================

ちなみに、データ・ファイルの配置場所(ファイル・システム、RAWデバイスやASMディスク・グループ)に関わらず非同期I/Oを制御する初期化パラメータ「DISK_ASYNCH_IO」も存在します。非同期I/Oを有効化したい場合には、必ずこのパラメータを「TRUE(デフォルト値)」に設定してください。

また、FILESYSTEMIO_OPTIONS初期化パラメータでダイレクトI/Oを有効化しても、OSのファイル・システム・キャッシュにサーバー・プロセスが読みに行かないだけであり、キャッシュはされ続けます。特にLinux OSでは物理メモリに空きがある限りキャッシュする動作になります。Oracleデータベースは、バッファ・キャッシュでもデータをキャッシュしているのでキャッシュの二重持ちの状態となり、物理メモリの使用効率的を考えると良くなさそうなのは明快です。よって、物理メモリに空きがある場合には、Oracleデータベースのバッファ・キャッシュのサイズを大きくすることをお勧めします。

■演習3.TBL2表とTBL3表から、それぞれ1レコード(col1=10000)だけ検索する実行時間を測定して下さい。

sqlplus / as sysdba
SQL> alter system FLUSH BUFFER_CACHE;

SQL> connect TRY/TRY
set timing on
select * from TBL2 where col1 = 10000;

      COL1 COL2
---------- --------------------------------------------------------------------------------
     10000 10000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

経過: 00:00:00.09

SQL> connect / as sysdba
SQL> alter system FLUSH BUFFER_CACHE;

SQL> connect TRY/TRY
set timing on
select * from TBL3 where col1 = 10000;

      COL1 COL2
---------- --------------------------------------------------------------------------------
     10000 10000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

経過: 00:00:07.67

 お待たせいたしました。いよいよSQLの性能測定になります。

TBL2表は100万件のレコードが格納されており、TBL3表はTBL2表からcreate as select文、もしくはinsert select文で作成したので同様に100万件のレコードが格納されています。セグメントのサイズも同じであることは第4回の演習で確認済みです。しかし、同じSELECT文にも関わらず、実行時間に大きな開きが生じていることが確認できています。当たり前と言えば当たり前なので、シニアなエンジニアの方にも申し訳ない例題ですね。すいません。。。

■演習4.演習3で比較した各SQLの実行計画を確認して実行時間の差を考察して下さい。方法は問いません。

sqlplus TRY/TRY
SQL> set autotrace on
SP2-0618: セッション識別子が見つかりません。PLUSTRACEロールが有効かを確認してください。
SP2-0611: STATISTICSレポートを使用可能にするときにエラーが発生しました。

 実行計画の確認方法は色々とありますが、まずは基本中の基本である「set autotrace on」を使用してみましょう。

勢い良くコマンドを実行してみましたが、上記のように「PLUSTRACEロールが付与されていない」という趣旨のエラーメッセージが表示されてしまった方が多いのではないでしょうか。このコマンドを使用するには事前準備が必要なのです。と言っても、それほど難しい内容ではありません。

sqlplus / as sysdba
SQL> @?/sqlplus/admin/plustrce.sql

ロールが作成されました。

SQL> grant PLUSTRACE to TRY;

権限付与が成功しました。

 PLUSTRACEロールを作成するスクリプトがOracle DatabaseをインストールしたOracle Homeディレクトリに保存されているので、「@?/sqlplus/admin/plustrce.sql」を実行します。簡単なオペレーションですが2点だけ補足しておきます。

sqlplusにおいて「?」はOracle Homeディレクトリに置換されます。つまり、「@?/sqlplus/admin/plustrce.sql」は、「@$ORACLE_HOME/sqlplus/admin/plustrce.sql」の意味となります。

また、非常に良くミスをする可能性が高いのがスクリプトの名称です。PLUSTRACEロールの作成スクリプトなので「plustrace.sql」だと思い込みがちですが、正確には「plustrce.sql」で「a」が欠けていることに注意です。

sqlplus TRY/TRY
SQL> set autotrace on
SQL> select * from TBL2 where col1 = 10000;

      COL1 COL2
---------- --------------------------------------------------------------------------------
     10000 10000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

実行計画
----------------------------------------------------------
Plan hash value: 1207070705

--------------------------------------------------------------------------------------------
| Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |              |     1 |    65 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TBL2         |     1 |    65 |     3   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | PK_TBL2_COL1 |     1 |       |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("COL1"=10000)

統計
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          4  physical reads
          0  redo size
        580  bytes sent via SQL*Net to client
        508  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

 「set autotrace on」を実行すると「set autotrace off」を実行するまでの間、そのセッションで実行したSQLの実行結果、実行計画と統計が出力され続けます。これ以外にも次のような設定が可能なので参考にしてみてください。

=====================================================
 SET AUTOTRACE ON EXPLAIN
> SQL文が実行され、SELECT文の場合は結果を表示。実行計画のみ表示。
 SET AUTOTRACE ON STATISTICS
> SQL文が実行され、SELECT文の場合は結果を表示。統計のみ表示。
 SET AUTOTRACE TRACEONLY
> SQL文が実行されるが、SELECT文の場合でも結果は表示されない。実行計画と統計を表示。
 SET AUTOTRACE TRACEONLY EXPLAIN
> SELECT文は実行されず、DML文は実行される。実行計画のみ表示。
 SET AUTOTRACE TRACEONLY STATISTICS
> SQL文が実行されるが、SELECT文の場合でも結果は表示されない。統計のみを表示。
=====================================================

ちなみに、PLUSTRACEロールの付与を行ったにも関わらず、「set autotrace on」実行時に相変わらずエラーが出力される場合があります。ロールでの権限付与は接続済みのセッションには反映されないという仕様があるので、新たにconnectしたセッションで試してみてください。

sqlplus TRY/TRY
SQL> set autotrace traceonly
SQL> select * from TBL3 where col1 = 10000;

実行計画
----------------------------------------------------------
Plan hash value: 2300171468

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   171 | 11115 |  4227   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TBL3 |   171 | 11115 |  4227   (1)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("COL1"=10000)

統計
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      15390  consistent gets
      15385  physical reads
          0  redo size
        712  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

 これで二つのSELECT文の実行計画が取得できました。色々と書きたいところですが、まだまだ先が長いので簡単に考察してみましょう。

実行計画は複数のOperationで構成されており、ツリー構造がインデントの深さで表現されています。今回扱う実行計画は非常にシンプルなので逆に「ツリー構造」がイメージし辛いかもしれません。基本的にはインデントが一番深いOperationから実行されますが、同じ深さのインデントのOperationが存在する場合には上から先に実行される点に注意です。詳しくは、津島博士のパフォーマンス講座の第4回をご参照ください。

TBL2表に対するSELECTでは、まずはPK_TBL2_COL1索引に対する索引スキャン「INDEX UNIQUE SCAN」で「COL1=10000」にヒットするROWIDを把握し、次にTBL2表に対する「TABLE ACCESS BY INDEX ROWID」でROWIDにヒットするレコードにのみアクセスしていることが読み取れます。ここで覚えておいて欲しいことは、Bツリー索引のリーフ・ブロックには、ROWIDと列データ値がセットで格納されているという点です。

一方、TBL3表に対するSELECTでは、TBL3表に対する「TABLE ACCESS FULL」で全レコードにアクセス(フルスキャン)して、「COL1=10000」にヒットするレコードをフィルタリングしていることが読み取れます。

つまり、TBL2表に対するSELECTは「索引検索」、TBL3表に対するSELECTは「全表検索」になっています。基本的に、数レコードにヒットするSELECTでは必要最低限のブロック・アクセスで完了する「索引検索」の方が高速ですし、統計に出力されている「db block gets + consistent gets」(メモリ(バッファ・キャッシュ)から読み込んだブロック数)や「physical reads」(ハードディスクから読み込んだブロック数)を比較しても、「索引検索」の方が圧倒的に少ないブロック数を記録しているので、演習3で測定した性能差の裏付けが可能になったかと思います。

では何故、TBL3表に対するSELECTが「全表検索」になってしまったのでしょうか。答えは簡単ですね。TBL3表のCOL1列に索引が作成されていないからです。一方、TBL2表のCol1列には主キーによるユニーク・索引が作成されていますよね。

「set autotrace」と並んで使用頻度が高い「explain plan for」での実行計画の取得例も以下に掲載しておきますので、参考にしてみてください。実行計画を取得したいSQLを「explain plan for」に続けて記述しますが、そのSQL文はSELECT文でもDML文でも実際には実行されず、実行計画を取得することが可能です。

sqlplus TRY/TRY
SQL> explain plan for
     select * from TBL2 where col1 = 10000;

解析されました。

SQL> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------
Plan hash value: 1207070705

--------------------------------------------------------------------------------------------
| Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |              |     1 |    65 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TBL2         |     1 |    65 |     3   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | PK_TBL2_COL1 |     1 |       |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("COL1"=10000)

「set autotrace」や「explain plan for」を使用する上での注意点を挙げておきます。

一つ目は、バインド変数を含んだSQL文では実際と異なる実行計画を出力することがあるという点です。バインド変数を含むSQL文の実行計画を確認したい場合は、演習6で紹介する「dbms_xplan.display_cursor」ファンクションを使用することをお勧めします。

二つ目は、パフォーマンスのトラブルが発生した後に「set autotrace」や「explain plan for」で実行計画を取得しても、トラブルが発生した際の実行計画ではない可能性があります。「set autotrace」や「explain plan for」は実行したその時点での実行計画であり、データベースの状態(正確には統計情報)が変化しているかもしれません。こちらも演習6で紹介する「dbms_xplan.display_cursor」ファンクションを使用して(SQL_IDを指定)、まさにトラブル発生時の実行計画を取得することが可能です。

■演習5.TBL2表とTBL3表から、それぞれ全レコードの総数を集計する実行時間を測定して下さい。

 

sqlplus / as sysdba
SQL> alter system FLUSH BUFFER_CACHE;

SQL> connect TRY/TRY
set timing on
select count(*) from TBL2;

  COUNT(*)
----------
   1000000

経過: 00:00:02.61


SQL> connect / as sysdba
SQL> alter system FLUSH BUFFER_CACHE;

SQL> connect TRY/TRY
set timing on
select count(*) from TBL3;

  COUNT(*)
----------
   1000000

経過: 00:00:10.58

 おやおや?何故だ?と思って頂きたい演習です。TBL2表とTBL3表は同じ100万行のレコードが格納されているので二つのSELECT文の結果もキッチリと100万となっているし、全レコードへアクセスしているのだから全表検索(実行計画では「TABLE ACCESS FULL」)だと思うけど、性能差が確認できたのは何故?という感じでお願いします。

では、ここまで学んだ実行計画の取得方法を駆使して、この原因の追及に挑戦しましょう。

■演習6.演習5で比較した各SQLの実行計画を確認して実行時間の差を考察して下さい。 方法は問いません。
トラブル・シューティングの場で非常に重宝する実行計画の取得方法「dbms_xplan.display_cursor」ファンクションを使用して原因分析を行いたいと思います。「dbms_xplan.display_cursor」ファンクションは演習4の最後で解説した「set autotrace」や「explain plan for」の注意点を回避し、さらに実行計画の各Operationの統計情報を併せて表示してくれる点が非常に強力なツールです。ここでは基本的な使用方法について解説するとして、詳細については、マニュアル「PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス」をご参照ください。

sqlplus TRY/TRY
SQL> select count(*) from TBL2;

  COUNT(*)
----------
   1000000

SQL> select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
User has no SELECT privilege on V$SESSION


SQL> connect / as sysdba
SQL> grant select on V$SESSION to TRY;
grant select on V$SESSION to TRY
                *
行1でエラーが発生しました。:
ORA-02030: 固定表/固定ビューでは選択のみ可能です。

 このファンクションを実行するには事前準備(権限の付与)が必要であり、権限が不足していると上記のようなエラーが発生します。また、V$SESSIONのSELECT権限を付与しようと試みるとORAエラーが発生してしまい手詰まり感を覚えるかと思います。

実行計画の比較という点から大きく脱線してしまいますが、これは非常に良い機会なので解説しておきますね。実は、V$SESSION等のデータ・ディクショナリ・ビューは実体ではなくSYNONYMであり、V$SESSIONと言う名前のVIEWは存在しないのです。

sqlplus / as sysdba
SQL> select SYNONYM_NAME, TABLE_NAME 
       from DBA_SYNONYMS 
      where SYNONYM_NAME = 'V$SESSION';

SYNONYM_NAME                   TABLE_NAME
------------------------------ ------------------------------
V$SESSION                      V_$SESSION

 上記のように、DBA_SYNONYMSデータ・ディクショナリ・ビューに問い合わせることで、V$SESSIONの実態が「V_$SESSION」であることが確認できます。

sqlplus / as sysdba
SQL>
grant select on V_$SESSION to TRY;
grant select on V_$SQL_PLAN to TRY;
grant select on V_$SQL to TRY;
grant select on V_$SQL_PLAN_STATISTICS_ALL to TRY;

 V$SESSIONへのSELECT権限を付与したい場合には、V_$SESSIONのSELECT権限を付与することになります。また、dbms_xplan.display_cursorファンクションを使用する為には、その他のデータ・ディクショナリ・ビューのSELECT権限が必要なので、上記を参考にしてみてください。

sqlplus TRY/TRY
SQL> select count(*) from TBL2;

  COUNT(*)
----------
   1000000

SQL> select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
SQL_ID  61z00xm8cw770, child number 0
-------------------------------------
select count(*) from TBL2

Plan hash value: 3652068985

------------------------------------------------------------------------------
| Id  | Operation             | Name         | Rows  | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |              |       |   586 (100)|          |
|   1 |  SORT AGGREGATE       |              |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| PK_TBL2_COL1 |   920K|   586   (2)| 00:00:01 |
------------------------------------------------------------------------------

 実行計画を確認したいSQL文を実行後、引数無しの状態で「dbms_xplan.display_cursor」ファンクションを実行すると、そのセッションで最後に実行された(直前の)SQL文の実行計画が出力されます。

V$SQLやV$SQLAREAデータ・ディクショナリ・ビューのSQL_IDカラムから取得したSQL_IDを第一引数に埋め込んで実行することで、特定のSQL文の実行計画を取得することも可能です。詳細については、マニュアル「PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス」をご参照ください。

sqlplus TRY/TRY
SQL> select count(*) from TBL3;

  COUNT(*)
----------
   1000000

経過: 00:00:10.58

SQL> select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
SQL_ID  9pytj15maxmjb, child number 0
-------------------------------------
select count(*) from TBL3

Plan hash value: 2630253756

-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |  4225 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| TBL3 |  1010K|  4225   (1)| 00:00:01 |
-------------------------------------------------------------------

 では最後に実行計画の比較を行ってみましょう。TBL3表に対するSELECTでは予想通り「TABLE ACCESS FULL」が選択されていることが確認できますが、TBL2表に対するSELECTでは「INDEX FAST FULL SCAN」という如何にも速そうな名称のOperationが選択されていることが確認できます。そうなのです。この「INDEX FAST FULL SCAN」が選択されたことで、TBL2表に対するSELECTの方が非常に高速に処理できた原因なのです。

前提知識として必要なのは、演習4でチラッと解説したBツリー索引のリーフ・ブロックには、ROWIDと索引キーの列データが格納されている点であり、これを理解した上で説明します。

「INDEX FAST FULL SCAN」は索引ブロックを全てスキャンし、その中からリーフ・ブロックに格納されている索引キーの列データを取り出すことで、全表検索で表ブロックにアクセスせずに、列データを取得可能です。一般的には、表セグメントのサイズよりも索引セグメントのサイズの方が小さいので、「INDEX FAST FULL SCAN」ではアクセスするブロック数が相対的に少なく高速化が期待できるのです。ちなみに、今回の演習環境でのPK_TBL2_COL1索引とTBL3表のセグメント・サイズは次のようになっており、圧倒的に索引セグメントの方が小さいことが確認できますね。

sqlplus TRY/TRY
SQL> select SEGMENT_NAME, SEGMENT_TYPE, TABLESPACE_NAME, BLOCKS 
        from USER_SEGMENTS
       where SEGMENT_NAME in ('PK_TBL2_COL1','TBL3');

SEGMENT_NAME             SEGMENT_TYPE       TABLESPACE_NAME                    BLOCKS
------------------------ ------------------ ------------------------------ ----------
TBL3                     TABLE              TBS_BIG                             16384
PK_TBL2_COL1             INDEX              TBS_BIG                              2176

 実行計画の取得、いかがでしたでしょうか。今回は簡単な順に3つの方法を紹介させて頂きましたが、これ以外にもSQLトレースやStatspack/AWRからの実行計画の取得、Oracle Database 11gから導入されたリアルタイムSQL監視、さらにはツール化したものではなくV$SQL_PLANの直接参照等の様々な手法が存在します。トラブル・シューティングの現場の状況次第では取得の事前作業が許可されないとか、再測定不可能という場面も出てきますので、いつでも全ての手法が使えるわけではありません。なので、是非とも活用シーンをイメージしつつ、ご自身で各手法を体験しておいて頂ければと思います。

長文にも関わらず最後まで読んで頂きまして、ありがとうございました。次回以降も頑張っていきますので、よろしくお願いします。
次回に予定しております演習問題を先に公開しておきますのでので、是非チャレンジしてみて下さい。お疲れ様でした。

 


ページトップへ戻る▲

 

しばちょう先生の試して納得!DBAへの道 indexページ▶▶