「あー、また結合(JOIN)の嵐だ……」
PostgreSQLの実行計画を見ていて、そんな溜息をついたことはありませんか? 結合するテーブルが3つや4つならまだしも、10個、15個と増えてくると、オプティマイザが「どの順番で結合するのが一番速いか」を考えるだけで、クエリの実行時間よりも長い時間を消費してしまうことがあります。
今日は、そんな「結合地獄」に陥った時に助けてくれる、PostgreSQLの隠れた(でも強力な)機能、GEQO(遺伝的クエリ最適化)についてお話ししましょう。
—
そもそも、なぜ最適化に時間がかかるのか?
PostgreSQLがクエリを実行する際、内部では「どのテーブルから結合すれば効率が良いか」という計算をしています。これを「動的計画法」と呼ぶのですが、これはテーブル数が増えれば増えるほど、計算コストが爆発的に増える仕組みなんです。
例えば、15個のテーブルを結合する場合、その組み合わせは天文学的な数になります。これをすべて全探索していたら、クエリを投げてからコーヒーを淹れて、飲み終わってもまだ結果が返ってこない……なんてことになりかねません。
そこで登場するのが、GEQO(Genetic Query Optimizer)です。
GEQO:自然界の知恵をデータベースに
GEQOは、名前の通り「遺伝的アルゴリズム」を使っています。生物が進化を通じて環境に適応していくのと同じように、クエリの実行計画も「たまたま良さそうな計画」をいくつか作り、それらを交配・突然変異させながら、短時間で「そこそこ優秀な実行計画」を見つけ出す手法です。
「完璧な最適解」ではないかもしれませんが、「実用的な速度で、十分に速い計画」を叩き出すのが彼らの仕事です。
—
GEQOを使いこなすための設定
PostgreSQLでは、デフォルトで結合テーブル数が12個を超えるとGEQOがONになるようになっています。でも、実務では「もっと少ないテーブル数でもGEQOを試したい」とか、逆に「複雑すぎてGEQOが変な計画を立てるから封印したい」というケースがあります。
1. GEQOを強制的にONにする
もし、「JOINが8個くらいで、なんか計画作成に時間がかかってるな」と感じたら、閾値を下げてみましょう。
— テーブルが8つ以上ならGEQOを動かす
SET geqo_threshold = 8;
2. GEQOのパラメータを調整する
GEQOが作る計画がイマイチだと感じたら、世代数や個体数をいじって調整可能です。
— 遺伝的アルゴリズムの調整(適宜試行錯誤が必要です)
SET geqo_generations = 100;
SET geqo_population_size = 500;
※注意点として、これらを上げすぎると、今度は「最適化のための計算」でCPUを食いつぶすことになるので、バランス感覚が大事です。
—
実務でGEQOと向き合う時の注意点
僕が後輩によく言うのは、「GEQOは最終手段だ」ということです。
まず疑うべきは、GEQOの調整ではなく、データベースの設計です。
- 統計情報は最新ですか?(`ANALYZE`は必須です)
- そもそもJOINしすぎではありませんか?(ビューの多段重ねなどは地雷です)
- インデックスは適切に効いていますか?
これらをすべて見直した上で、それでもなお「どうしようもなく結合数が多いクエリ(例えばBIツールが自動生成するような巨大クエリ)」と戦わなければならない時、初めてGEQOのパラメータをいじります。
現場からのアドバイス
もしGEQOを有効にしても性能が出ない場合、`SET geqo = off;` とした状態で `EXPLAIN` を見てみてください。そこで出た計画と、GEQOが選んだ計画を比較すると、どこで迷走しているのかが浮き彫りになります。
まとめ
GEQOは、PostgreSQLというエンジンの「奥の院」のような機能です。日常的に触る必要はありませんが、いざという時にこの存在を知っているだけで、絶望的なクエリを解決できる選択肢が増えます。
「全探索で完璧を求める」のではなく、「進化の力で現実的な落とし所を見つける」。
エンジニアとしての悩みも、たまにはこのGEQOの考え方で割り切ってみると、案外うまくいったりするかもしれませんね。
さて、そろそろ次のクエリチューニングに行ってきます。皆さんも、良いデータベースライフを!
コメント