「デフォルト値」という名の罠:cpu_tuple_cost と最適化の深淵
PostgreSQLのチューニングにおいて、`work_mem` や `effective_cache_size` をいじるのは、いわば「基礎工事」のようなものだ。しかし、クエリの実行計画がどうしても直感に反する動きをするとき、我々エンジニアはパラメータの深層へ潜る必要がある。
今日は、そんな深淵の一つ、`cpu_tuple_cost` について話をしようと思う。
なぜ、プランナは「間違った選択」をするのか
PostgreSQLのプランナは、コストベースのオプティマイザだ。しかし、この「コスト」はあくまで見積もりに過ぎない。特に、大量のタプルをスキャンする際、デフォルトの `cpu_tuple_cost = 0.01` という値が、現代のハードウェア環境やデータセットの特性と乖離していることは珍しくない。
例えば、複雑なJOINや、大量の行をフィルタリングするクエリで、インデックススキャンではなくシーケンシャルスキャンが選ばれてしまうケース。あるいはその逆。なぜプランナはそれを選ぶのか。それは、プランナが「1タプルを処理するコスト」を、あなたのマシンの実際のCPUパフォーマンスに対して過小(あるいは過大)に見積もっているからだ。
cpu_tuple_cost が支配する領域
`cpu_tuple_cost` は、プランナがタプルを処理する際のCPUコストを定義する。具体的には、フィルタ条件の評価や、プロジェクションの作成などだ。
ここでのポイントは、この値が 「行数が増えれば増えるほど、雪だるま式に実行計画の選択に影響を与える」 という点だ。
- 小規模なテーブル: ここでは `cpu_tuple_cost` の影響は軽微だ。I/Oコスト(`seq_page_cost` など)が支配的になるためだ。
- 数百万行を超えるテーブル: ここが戦場だ。`cpu_tuple_cost` がわずかに低いだけで、プランナは「シーケンシャルスキャンしてフィルタしたほうが、ランダムアクセスを伴うインデックススキャンより安い」と判断しやすくなる。
特に、NVMe SSDが普及し、ランダムアクセスのコストが劇的に下がった現代において、PostgreSQLのデフォルト値が「ディスクI/Oがボトルネックだった時代」のまま止まっていることは、我々が意識すべきギャップだ。
パフォーマンスチューニングの実践的アプローチ
もし、あなたのシステムで「明らかに効率の悪いフルスキャン」が頻発しているなら、まずは `EXPLAIN (ANALYZE, BUFFERS)` を見てほしい。見積もりコストと実際の実行時間に乖離がある場合、`cpu_tuple_cost` を調整する価値がある。
ただし、安易にグローバルな値を変更するのは禁物だ。これはデータベース全体、つまり全てのクエリに影響する劇薬だからだ。
1. 特定のセッションでのテスト
まずは、特定のクエリに対して、`SET LOCAL cpu_tuple_cost = 0.005;` のように値を下げて実行計画を見てみてほしい。もしこれで、プランナがより適切なインデックスを選ぶようになるなら、それはモデルが現実の処理能力に近づいたという証拠だ。
2. 計算の重み付けを考える
最近のPostgreSQLのバージョンでは、`cpu_operator_cost` や `cpu_index_tuple_cost` も存在する。これらとのバランスが重要だ。単純に `cpu_tuple_cost` だけをいじるのではなく、「なぜそのタプル処理が重いのか」を考える必要がある。JSONB型の複雑な操作や、重い演算子が含まれているなら、`cpu_operator_cost` の調整の方が効くこともある。
最後に:魔法の数字はない
「じゃあ、結局いくらに設定すればいいんだ?」という質問が聞こえてきそうだが、残念ながら答えはない。
データベースのチューニングに「ベストプラクティス」という言葉は、往々にして「思考停止」の別名だ。ストレージの種類、CPUのクロック数、メモリ帯域、そして何より「テーブルの行数と検索頻度のバランス」。これらすべてが、その環境における最適な `cpu_tuple_cost` を決定する。
私がアドバイスできるのは、「プランナを信じすぎないこと」だ。
デフォルト値は、あくまで一般的なケースを想定した「平均値」に過ぎない。あなたのデータベースは、世界にたった一つしかない、独自のデータ構造とクエリ負荷を持っているはずだ。
その特性を理解し、パラメータを微調整し、実行計画が「狙い通りの動き」に収束したときの快感。それこそが、データベースエンジニアという職種の醍醐味ではないだろうか。
皆さんのクエリが、今夜もインデックスの上を軽快に駆け抜けることを願っている。
コメント