実行計画のキャッシュについて2 - 各RDBMSについて
SQL文の動的生成を避けるには、実行計画のキャッシュを意識しなければなりません。
そこで、RDBMS毎にキャッシュのやり方が違いますからその違いと、「本当はこういう構造にすれば良いのに」という提案について書きます。
目次
各RDBMSの実行計画のキャッシュ
MySQL
実行計画はキャッシュしない。
SQLと結果をキャッシュする。
PostgreSQL
実行計画のキャッシュはセッション単位。プリペアードステートメントにするとキャッシュする。
SQLServer
基本的に実行計画をキャッシュする。
RECOMPILE オプションでキャッシュをキャンセルできる。
Oracle
基本的に実行計画をキャッシュする。
BIND PEEK(最初に実行されたバインド変数で実行計画を作りキャッシュする)
ADAPTIVE CURSOR SHARING を有効にしていれば(嫌う人が多く有効にされていないことが多いが)、最初に設定されたバインド変数と外れた値が設定され、保存されている実行計画が不適切だと判断すると、同じSQLに対して、変数の範囲で複数の実行計画をキャッシュし使い分けます。
Oracleはよくできているが逆!
ADAPTIVE CURSOR SHARING は非常によくできた仕組みです。
しかし、考え方が逆でしょう。
Oracleの処理の概要
Oracleの処理はかなり端折っていますが、以下のように処理されます。
前回より遅いというのがどれぐらいで判断するのかはっきりしないが、同じSQLを実行された回数の10%ぐらいという実験結果もあるようです。
いずれにしても、少なくとも1度は不適切な実行計画が実行される訳です。
私が欲しい処理
Oracle の逆の処理を行うのです。遅いか判断するのではなく、実行計画が同じか判断すればよいでしょう。
バインド変数は範囲で持っているはずなので、作成した実行計画が同じならバインド変数の範囲を拡張していけば最終的には、最適化されたキャッシュになります。
1回目に実行計画を作ったときは、ユニークキーに対して等号(=)で判断しているバインド変数は、範囲をLowValからHighValまでとしておけば良い。NULL値かどうかは別途持つ。
まとめ
前回に書いたようなやり方では、RDBMSが持つ実行計画のキャッシュが使えません。
これは実にもったいなく、Oracle の ADAPTIVE CURSOR SHARING はよくできているけれど、必ず不適切な実行計画を実行しなければいけない時点でダメでしょう。
現実に、ADAPTIVE CURSOR SHARING を無効にして運用しているプロジェクトの方が多いようです。
実行計画をキャッシュしつつ、不適切な実行計画を使うことをできる限り避けるには、Oracle の逆の考え方にするのがベストと私は考えています。
そうならなかったのは、前回書いたような当たり前のコーディング(私は20年以上前からそうしている)が、想定されていないからです。
想定していたら、実行計画のキャッシュは必ず問題になるし、O/Rマッパーなるものは生まれなかったでしょう。
「O/Rマッパー」という呼び名がなかった新人の頃に私も企画し、「工夫したら分岐は避けられる」と気づいて、企画を放棄したのですから。