ablog

不器用で落着きのない技術者のメモ

バインド変数を使えば必ず SQL が速くなるというわけではない

門外不出のOracle現場ワザ (DB Magazine SELECTION) のスバラシイ記事をオラクルのウェブサイトから引用します。
Oracle Database の「バインドピーク」機能についてとバインド変数ではなくリテラルで記述したほうが適切なケースなどについて書かれてます。

Oracle9i以降のリリースでは、デフォルトでバインドピークと呼ばれる機能が有効になっています。 9iより前のリリースでは、WHERE句にバインド変数を使用した場合、SQL文のパース時には当該変数にセットされる値が分からないため、行の選択率を内部デフォルト値で決定していました。


...


バインドピークは、ハードパースでオプティマイザが実行計画を生成する際、バインド変数にセットされた実際の値を「覗く(peek)」機能です。これにより、リテラルSQLの場合と同じように、値の分布を考慮して(ヒストグラムも利用して)、行の選択率を計算できるようになります。内部デフォルト値ではなく、ユーザーが指定した実際の値と統計を利用できるため、より適切な実行計画の選択が期待できます。
ただし、バインド変数を使用したSQLの実行計画(アクセスパスなど)は、共有メモリ内の1つの共有カーソルとしてほかのユーザーにも共有されます。つまり、あるSQLを最初に実行したユーザーがセットした値に実行計画が最適化され、その後、同じSQLを実行しようとするユーザーにはすべて同じ実行計画が適用されます。例えば、最初のユーザーの範囲指定に対してフルスキャンが最適だったとすると、次のユーザーが1件だけヒットするような条件指定をしたとしてもフルスキャンとなります。


...

  • バインド変数を使用する目的は、同じ構文のSQLの実行計画を1つに統一し、リソース使用効率を向上することである
  • 1つの実行計画がすべてのバインド値に対して最適となるはずはなく、現実的にはユーザーが使用する「大部分」(例えば90%)の値で良好な性能が得られれば可とすべきである
  • 1つの実行計画が「大部分」の要求を満たせない場合、複数の実行計画を使い分ける必要がある。この場合はリテラルで記述したほうが適切
    • DWH系のシステムはこのケースに当てはまる
    • OLTP系では、このようなSQLの割合をできるだけ少なくする必要がある
  • バインドピークがONの場合、例外的な値に実行計画が最適化される可能性があり、「大部分」の性能を劣化させるリスクがある。ただし「大部分」の側の値に最適化された場合は、バインドピークがOFFの場合には得られないような効率的な実行計画が生成される可能性がある
  • バインドピークがOFFの場合、最適性は薄れるが、実行計画変動のリスクは少ない
    • 等価検索は1/NDVが選択率とされるため、ヒストグラムがない状態のリテラルSQLと同じ精度
    • 範囲検索のデフォルトの選択率が5%であるため、比較的索引が使われやすいと言える
    • 索引の選択や結合順序など、最適なものが選択されない可能性がある。その場合、ヒントによる強制が必要となる

バインドピーク機能をOFFにするには、「_optim_peek_user_binds」パラメータをFALSEにします。このパラメータは、いわゆる「隠しパラメータ」で、通常は「サポートセンターの指示なしでは設定すべきでない」パラメータです。しかし、サポート契約のあるユーザーに対しては公開されており、「機能を理解した上で使用する場合はサポートされる」とされています注8。
なお、バインドピークについては10gのマニュアルでは「ユーザー定義のバインド変数の照合」と記述されています。

注8:KROWN#:81865「Oracle9i Database以降におけるSQL最適化機能(bind peek)に関する留意事項」

http://www.oracle.com/technology/global/jp/pub/jp/db_magazine/mongai/chapter4_2.html