
SQLチューニング(第5回)「サブクエリ動作方式を理解すること」(3/8)
「サブクエリと性能問題の理解」3回目の今回は、「FILTER動作方式」についてです。それでは、早速内容に入ります。
2.1.2 サブクエリ動作方式を理解すること
サブクエリの動作方式は大きくFilter動作方式と結合動作方式で分けられる。また、どんな動作方式で処理されるかによりSQLの性能差が大きく発生する可能性がある。 したがってサブクエリが効率的な実行ができるように動作方式を理解して制御するべきである。
今回は、サブクエリの動作方式を理解する一方、実行計画(動作方式)を制御できるヒントを調べて直接実行計画を制御することにより、サブクエリーによる性能問題が発生しないようにSQLを作成する方法に対して調べてみる。
2.1.2.1 FILTER動作方式
Filter動作はMain SQLで抽出されたデータの件数だけでサブクエリーが繰り返し実行され、処理される方式である。つまり、Main SQLの抽出結果について、各行ごとにサブクエリ結合接続値(以下、Inputの値)に対して処理実行後、結果がTRUEの場合、データを抽出する。
たとえば、Main SQLの抽出結果が100万件であれば、サブクエリは、最大100万回実行される。ところが、もし、上記のようなSQLのサブクエリの結合接続カラムに適切なインデックスがない場合はどうなるだろうか? 読者の方々もおわかりの通り、サブクエリを100万回繰り返しFull Table Scanを実行することになるだろう。このような実行計画を持つSQLが最悪の性能を示すという点はもちろんのことDBサーバの性能にも深刻な悪影響を与える。
反面、Main SQLの抽出件数が2件である場合Filter動作方式で処理されるならば、サブクエリは最大2回だけ実行すれば良い。 その上サブクエリの結合連結カラムで構成されたUnique Indexが存在するならば、非常に効率的な処理ができると考えられる。
ところで「Main SQLの抽出結果が多い場合Filter動作方式で処理されるならば、常に性能が良くないだろうか?」 という疑問が発生する。 結論から言えば、常にそうはならない。 Main SQLの抽出結果が多くてもサブクエリのInput値が皆同一ならば、あたかもMain SQLで1件だけ抽出したことのように1回だけ実行するので性能問題が発生しない。 これはOracleが内部的にFilter動作方式に対してFilter Optimization(※注1)と呼ばれる最適化作業を実行するためである。 しかし一般的にサブクエリのInput値が皆同じ値である確率は非常に稀である。 したがって抽出件数が多い場合サブクエリをFilter動作方式で処理する場合、性能上非効率的な場合が多く存在する。 したがってFilter動作方式で実行される場合Input値の種類が少なくて性能に有利なのかを必ず確認しなければならない。
(※注1) Filter Optimization:サブクォリを実行するInput値をCache値や、同じInput値が使われる場合、追加的な読み取り作業なしでCacheなった値を使う。
これからFilter動作方式がMain SQL抽出結果とInput値の種類によって、どのようにSQLの性能が変わるのか、三種類のテストを通じて詳しく調べてみることにする。
それでは、テスト スクリプトを実行することにしよう。
Script. サブクエリテスト
最初のテストはMain SQLの抽出結果が多くてInput値がUniqueある場合である。
テスト[1]. Main SQLの抽出件数が多く、Input値がUniqueである場合
上記SQLの実行計画を説明する。 まず実行計画にFILTERオペレーションがあるためにFilter動作方式で実行されたということがわかる。
最初にSUBQUERY_T2テーブルをFull Table Scanで読んで“c1 >=:b1 AND c1 <=:b2”条件に満足するデータを抽出するが、合計380,001件が抽出される。 この中で最終データを抽出するためにはサブクエリの結果も満足するかを確認しなければならない。 したがってサブクエリでC1値を渡した後サブクエリに結果値が存在するのか確認する。 この時サブクエリはC1値をSUBQUERY_T1_IDX_01インデックスを使って合計380,001回反復問い合わせて、最終データ230,001件を抽出した。
テスト[1]のSQLのトレース結果を見れば、Main SQLの抽出結果が38万件なのに、Filter動作方式で実行されて見たらサブクエリが38万回も繰り返し実行された。 結局SQL全体I/O発生量の大部分はサブクエリで繰り返し使うSUBQUERY_T1_IDX_01インデックスで発生したのである。
二番目のテストはMain SQLの抽出件数が少ない場合である。
テスト[2]. Main SQLの抽出件数が少なくて、Input値がUniqueである場合
上記の場合もまた、Filter動作方式で実行されるが、Main SQLの条件が効率的なので抽出されるデータ件数が合計5件にしかならない。 それでサブクエリも5回だけ実行するので、SQLの性能がテスト[1]と比較すると非常に良好である。
最後にMain SQLの抽出結果は多いが、Input値(T1.C5)の種類が少ない(26種類)場合の性能を確認してみることにしよう。
テスト[3]. Main SQLの抽出件数は多いが、Input値の種類が26種類である場合
(※注2)スカラーサブクエリ、サブクエリで使うMulti BufferのSizeを調節するヒースはパラメーターであり、基本値が64KBである。 Sizeが小さい場合Multi Buffer内でAge Outが発生して、値の種類よりサブクエリがさらに多く実行することができる。 テストもAge Outが発生して値の種類(26種類)よりさらに多く実行されることによってSizeを変更してテストした。
レース結果を見れば、Main SQLの抽出件数は380,001件で相当多いが、Input値の種類は26種類に過ぎない。 だが、テスト[3]のサブクエリはMain SQLの抽出結果に対して毎回実行しなかった。 その理由はサブクエリのInput値をCacheして、Input値が同じである場合サブクエリを実行しないためである。 それによりサブクエリの実行回数はInput値の種類の26回だけ実行され性能が良好だということが分かる。
ここまで三種類のテストを通じてサブクエリがFilter動作方式で実行される場合Main SQL抽出結果とInput値の種類によって性能がどのように変るのか調べた。
整理すれば、Filter動作方式はMain SQLの抽出結果が多くて、サブクエリに提供する値(Input値)の種類が多いならば、性能が良くないということが分かった。 しかしMain SQLの抽出件数が少なかったり、Main SQLの抽出結果が多かったとしてもInput値の種類が少ない場合Filter動作方式で実行されるSQLも性能が良好だということが分かった。
Filter動作方式の場合、常にMain SQLが先に実行されて、サブクエリはMain SQLで抽出されたデータの値を受けて毎回確認する方式で実行される。 このようにFILTERオペレーションは常に一つの方法だけを守るので多様な状況で柔軟に対処するのが難しい動作方式だと見ることができる。 このような理由でサブクエリがFilter動作方式で実行される場合SQL性能が良くない場合が多い。
もし、SQLの実行計画を点検してサブクエリがFilter動作方式で実行されているならば、先にサブクエリの結合連結カラムにインデックスが存在するのか確認しなければならない。 なぜなら、サブクエリがFilter動作方式で実行されるが、Full Table Scanで処理しているならば深刻な性能問題が発生する可能性があるためである。
今回の「FILTER動作方式」の解説は理解できましたでしょうか?次回は、サブクエリの動作方式の2回目「結合動作方式」についてです。ご期待ください。それでは、See you Next ^-^