コストモデルを「ハック」する:PostgreSQLプランナと向き合う究極のチューニング
PostgreSQLのパフォーマンスに悩まされ、最終的に `EXPLAIN ANALYZE` の深淵を覗き込んだ経験がある人なら、一度はこう思ったはずだ。「なぜプランナは、明らかに非効率なシーケンシャルスキャンを選択したのか?」と。
多くのエンジニアは、まずインデックスの再構築や統計情報の更新を疑う。もちろんそれは正しい。だが、もしデータベースの物理構成が標準的でない場合――例えば、超高速なNVMe SSDを積んでいるのにHDD前提のコスト設定で動かしているなら――いくらクエリを磨いても、プランナは「最適ではない未来」を選び続ける。
今回は、PostgreSQLのコストモデルの心臓部、`cost_param` について少し深く掘り下げてみよう。
—
コストモデルは「地図」に過ぎない
まず前提として、`seq_page_cost` や `random_page_cost` は、決して「現実の処理時間」を計測しているわけではない。これらはプランナが実行計画を比較検討するための「相対的な重み付け」だ。
- `seq_page_cost` (デフォルト: 1.0): 順次読み取りのコスト
- `random_page_cost` (デフォルト: 4.0): ランダム読み取りのコスト
この4倍という比率は、伝統的なHDD(磁気ディスク)のシーク時間を考慮したレガシーな名残だ。しかし、現代のNVMe SSDの世界ではどうだろう? 物理的にヘッドを動かす必要がないため、ランダムアクセスとシーケンシャルアクセスの速度差は、かつてほど大きくない。
ここで `random_page_cost` をデフォルトの4.0のまま放置しておくことは、プランナに「インデックスを使うのは高くつくから、なるべくテーブル全体をなめた方がいい」という誤ったバイアスを与え続けていることに等しい。
なぜデフォルト値で「事故」が起きるのか
パフォーマンストラブルの現場でよく見るのが、大きなテーブルに対するインデックススキャンが無視されるケースだ。
プランナは「ランダムアクセスがシーケンシャルアクセスの4倍コストがかかる」と計算しているため、インデックスを使ってポツポツとデータを探すよりも、いっそのことシーケンシャルスキャンで全件なめてしまった方が「安上がりだ」と判断してしまう。
SSD環境であれば、この比率を 1.1 〜 1.5 程度まで下げてみるだけで、実行計画が劇的に改善することがある。これは魔法ではない。プランナに「今の環境はランダムアクセスが極めて高速だ」という事実を伝えた結果に過ぎない。
チューニングの作法:闇雲な変更は禁物
ただし、これらをいじる際には一つだけ肝に銘じてほしい。「コストパラメータはグローバルに効く」ということだ。
特定のクエリのためだけに `random_page_cost` を変更すれば、他のクエリの実行計画までガラリと変わってしまう可能性がある。あるテーブルではインデックスが効くようになっても、別のテーブルでは逆にシーケンシャルスキャンが選択されすぎて悲鳴を上げることがある。
もし特定のテーブルやインデックスだけに影響を与えたいなら、テーブル単位のパラメータ設定を検討しよう。
ALTER TABLE your_table_name SET (random_page_cost = 1.1);
こうすることで、環境全体に副作用を与えることなく、ピンポイントでプランナの判断を矯正できる。これが、熟練のエンジニアが取るべき「外科手術」の手法だ。
最後に:計測なき最適化はただの勘
ここまで熱く語っておいて何だが、一番大切なのは「計測」だ。
設定値を変更する前には、必ず `EXPLAIN (ANALYZE, BUFFERS)` を取り、実際の実行時間とキャッシュヒット率を確認してほしい。また、`pg_stat_statements` を使って、変更前後でシステム全体のクエリ傾向がどう変化したかを追跡することも忘れないように。
コストモデルを調整するということは、PostgreSQLの脳内に直接介入するようなものだ。少しの変更で劇的な変化を生むこともあれば、思わぬ落とし穴にハマることもある。だからこそ、面白い。
あなたのデータベースが、最も効率的なルートを自ら見つけ出せるようになることを願っている。それでは、また。
コメント