【実務・中級編】 GEQO (遺伝的クエリ最適化) – PostgreSQL

「なあ、ちょっといいか? 今日のDBチューニングの件なんだけどさ。」

現場でPostgreSQLをいじっていると、一度はぶつかる壁があるんだよね。「結合(JOIN)するテーブル数が多すぎて、クエリがいつまで経っても返ってこない」っていうあの現象。

ログを見てみると、実行計画(EXPLAIN)の算出だけで数秒、あるいは数十秒かかっている……なんて経験、ないかな?

今日は、そんな時の「劇薬」とも言えるGEQO (Genetic Query Optimizer) について話そうと思う。これ、教科書通りに理解しようとすると眠くなるんだけど、実務で使うなら「いつ封印を解くか」さえ知っていれば、かなり頼れる相棒になるんだ。

—

そもそも、なんで結合が増えると重くなるのか?

PostgreSQLのオプティマイザは、基本的には「全探索(Dynamic Programming)」で一番速い実行計画を探そうとする。

例えば、テーブルが3つなら組み合わせは少ない。でも、結合テーブルが10個、15個と増えていくと、その組み合わせは爆発的に増える。CPUをぶん回して計算し尽くせば、理論上は「最も速い計画」が見つかるけれど、そこに辿り着く前に「計画を立てる時間」でタイムアウトになっちゃうわけだ。

そこで登場するのが GEQO。これを使うと、全探索を諦めて「遺伝的アルゴリズム」を使って、そこそこ良い感じの実行計画を短時間で見つけてくれるようになる。

GEQOのスイッチはどこにある?

PostgreSQLには `geqo_threshold` という設定値がある。これは「何テーブル以上結合するときにGEQOを動かすか」というしきい値のことだ。デフォルト値は確か `12` だったはず。

つまり、12テーブル以上結合すると、勝手にGEQOが発動する仕組みになってるんだ。

ただ、ここが落とし穴なんだけど、「デフォルトの設定が必ずしも最適とは限らない」ってこと。環境によっては、10テーブルくらいでも全探索が限界だったり、逆に「GEQOで出した計画よりも、少し待ってでも全探索させたほうが速い」なんてケースも多々あるんだよ。

実践:GEQOをコントロールする

現場で困ったときは、まずここを疑ってみるのが定石だ。

1. 現在の設定を確認する

SHOW geqo;
SHOW geqo_threshold;

2. 特定の重いクエリでだけ試してみる

もし、特定のクエリが遅くて、それが「計画作成(Planning Time)」で詰まっているなら、まずはセッションレベルでGEQOをいじってみよう。

— 一時的にGEQOをオフにしてみる(全探索させる)
SET geqo = off;
EXPLAIN ANALYZE SELECT … — 実行計画がどう変わるか見てみる

— 逆に、しきい値を下げてGEQOを積極的に使わせる
SET geqo_threshold = 8;
EXPLAIN ANALYZE SELECT …

先輩からのアドバイス:GEQOは「最後の手段」だと思え

正直に言うよ。GEQOは、万能薬じゃない。

遺伝的アルゴリズムだから、どうしても「最適ではない計画」を掴んでしまうリスクがある。全探索なら確実に一番速い道を見つけられるところを、GEQOは「まあ、この辺でいいよね?」と妥協するわけだ。

もし君が担当しているシステムでGEQOのお世話になりそうなら、まずは以下の順で手を打ってみてほしい。

1. インデックスを見直す: そもそも結合条件にインデックスが効いていないなら、GEQOに頼る前にやることがあるはずだ。
2. クエリの構造を見直す: サブクエリを多用しすぎていないか? `WITH` 句(CTE)で中間結果を整理できないか?
3. 統計情報を更新する: `ANALYZE` をかけて、オプティマイザに正しい情報を与えてやれば、全探索でも意外と速く終わることは多い。

それでもダメで、かつクエリが「現実的な時間内に実行計画を立てられない」という物理的な限界に達しているなら、その時こそ `geqo` の出番だ。

まとめ

GEQOは、PostgreSQLが持っている「最後の安全装置」みたいなものだ。普段は意識しなくていいけれど、いざという時にその存在を知っているだけで、現場のトラブル解決スピードが段違いになる。

まずは `geqo_threshold` を今の環境に合わせて少し調整してみる、というところから始めてみるといいよ。

もし「GEQOをONにしたら劇的に速くなった!」なんてことがあったら、ぜひ教えてくれ。その時は、そのクエリの構造を一緒に解析しようぜ。

それじゃ、また。現場からは以上です!

コメント

タイトルとURLをコピーしました