【テクニカル・上級編】 プランナの定数設定 – PostgreSQL

クエリプランナの「脳内コスト」を疑え:cpu_tuple_costが隠し持つ真実

PostgreSQLのクエリプランナと長く付き合っていると、時折「なぜ、この単純なインデックススキャンを選ばず、コストの高いシーケンシャルスキャン(Seq Scan)を強行するのか?」という理不尽な事態に遭遇するはずだ。

統計情報の更新(`ANALYZE`)は完璧。インデックスも適切に貼られている。それなのに、プランナが導き出す「予測コスト」が実測と乖離しているとき、我々が最後に立ち返るべき場所は、設定ファイル(`postgresql.conf`)に鎮座する「コスト定数」のチューニングだ。

今日は、特にCPUコストの係数である `cpu_tuple_cost` や `cpu_index_tuple_cost` が、内部的にどうクエリの選択を歪めてしまうのか、その深淵を覗いてみよう。

—

コストモデルという名の「地図」

PostgreSQLのプランナは、クエリ実行の総コストを「I/Oコスト」と「CPUコスト」の合算で算出する。この時、実行計画の選択基準となるのが、以下のパラメータ群だ。

  • `cpu_tuple_cost`: 1タプルを処理するコスト。
  • `cpu_index_tuple_cost`: インデックススキャン中に1タプルを処理するコスト。
  • `cpu_operator_cost`: 演算子や関数を適用するコスト。

これらはデフォルト値が0.01や0.0025といった非常に小さな値に設定されているが、重要なのは「絶対値ではない」ということだ。これらはあくまで、プランナが異なるプラン(例えばIndex Scan vs Seq Scan)を比較するための「重み付けの定数」に過ぎない。

なぜデフォルト値では足りないのか

デフォルトのコスト設定は、現代のハードウェアの進化、特に「巨大なメモリ」と「NVMe SSDの爆速I/O」を前提としていないケースが多い。

例えば、`cpu_tuple_cost` を安易にデフォルトのまま放置していると、プランナは「CPU負荷なんて大したことはない」と過小評価し、インデックスを引いてランダムアクセスを繰り返すよりも、フルスキャンでメモリ上に一気にデータをロードして処理するほうが速い、という極端な判断を下しがちだ。

特に、次のような環境ではこれらの定数の調整が急務となる。

  • 大規模なテーブルのJOIN: 内部的にハッシュ結合のコストが過小評価され、Nested Loopを選ばせたいケースでハッシュが選択される。
  • 複雑なフィルタ条件: `cpu_operator_cost` が低すぎて、コストの高い関数を用いた計算をWHERE句に含めても、プランナがそれを「誤差」として無視してしまう。

パフォーマンストラブルシューティングの勘所

現場で「プランナがクソな選択をする」という叫びを聞いたとき、私はまず `EXPLAIN (ANALYZE, BUFFERS)` を叩く。ここで重要なのは、「プランナが予想したコスト」と「実際の実行時間」のギャップだ。

もし、予想コストに対して実時間が圧倒的に長いなら、それは「CPU負荷」の過小評価だ。特にJSONB操作や複雑な正規表現、あるいはユーザ定義関数(UDF)を多用している場合、デフォルトの `cpu_operator_cost` では全く太刀打ちできない。

調整の鉄則

これらの値を変更する際は、以下の手順を強く推奨する。

1. 段階的な引き上げ: 0.01を0.05にするような極端な変更は避ける。0.005刻みで、実際のクエリプランがどう遷移するかを追う。
2. テーブル単位でのオーバーライド: グローバルな `postgresql.conf` をいじるのは最終手段だ。まずは `ALTER TABLE … SET (autovacuum_vacuum_scale_factor = …)` のように、特定のテーブルやセッションレベルで `SET LOCAL cpu_tuple_cost = …` を試し、影響を検証する。
3. ハードウェアの特性を考慮する: CPUコアが並列処理に長けているなら、`parallel_tuple_cost` もセットで見直す必要がある。

最後に:プランナを信じすぎないこと

PostgreSQLのプランナは非常に優秀だが、所詮は「統計とコスト定数という地図」を頼りに走る旅人に過ぎない。もしあなたのデータベースが、標準的なワークロードから外れた「異常なデータ分布」や「極端に重い演算」を抱えているなら、その地図自体を現場に合わせて書き換えることこそが、エンジニアの腕の見せ所だ。

コスト定数のチューニングは、いわばエンジニアがPostgreSQLの脳内に直接アプローチする外科手術のようなものだ。慎重に、しかし大胆に。クエリが最適化された瞬間の、あの静かな感動を追い求めてほしい。

皆さんのクエリが、今日も最短経路で駆け抜けることを願っている。

コメント

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