「プランナのさじ加減」を理解する:PostgreSQLのCPUコスト定数と付き合う技術
やあ。最近、PostgreSQLのパフォーマンスチューニングで悩んでるって聞いたよ。
「インデックスも貼ったし、`VACUUM ANALYZE`も定期的に回してるのに、なぜか実行計画が変な方を選ぶ……」なんて経験、一度はあるよね。
実はPostgreSQLのオプティマイザ(プランナ)は、内部で「このクエリを実行するのにどれくらいのコストがかかるか」を数値化して比較しているんだ。この計算式の中核を担っているのが、`postgresql.conf`にひっそりと鎮座する「コスト定数」たちだ。
今日は、その中でも特に重要で、かつ「沼」にハマりやすいCPU関連のコスト定数について、現場の知見を交えて話していくよ。
—
なぜデフォルト値が「最適ではない」ことがあるのか
まず大前提として。PostgreSQLのコスト定数のデフォルト値は、あくまで「一般的なハードウェア」を想定した汎用的なものだ。
もし君の環境が、超高速なNVMe SSDを積んだ最新鋭のサーバーなのか、それとも安価なクラウドインスタンスなのかによって、最適な計算式は変わってくる。
特に今回紹介する以下のパラメータは、クエリの実行計画に直接「重み付け」をするものなんだ。
- `cpu_tuple_cost`: 行(タプル)を処理するコスト。
- `cpu_index_tuple_cost`: インデックス経由で行を処理するコスト。
- `cpu_operator_cost`: クエリ内の演算(WHERE句の条件比較など)にかかるコスト。
—
「プランナの判断」を左右する定数の正体
例えば、プランナが「フルテーブルスキャン」と「インデックススキャン」のどちらが速いかを判断する時、内部ではこんな計算が働いている。
コスト = (ページ読み込みコスト) + (行を処理するCPUコスト)
もし君のデータベースが「インデックスを貼っているのに、なぜかフルスキャンを選択する」という挙動を見せているなら、プランナは「インデックスを使って大量のタプルを読み出すよりも、シーケンシャルにメモリに乗せて回した方がCPUコスト的に安い」と判断している可能性が高い。
ここで調整の出番だ。
1. `cpu_tuple_cost` を調整するケース
デフォルトは `0.01` だ。この値を少し上げると、プランナは「行を走査するコストは高い」と認識するようになる。結果として、「できるだけ行に触りたくないから、インデックスを積極的に使おう」という思考回路に誘導できるんだ。
2. `cpu_index_tuple_cost` を調整するケース
デフォルトは `0.005`。インデックスのスキャンコストだね。
もしインデックスの断片化が進んでいたり、インデックスの階層が深すぎてアクセス負荷が高いと感じるなら、この値を少し上げることで、過度なインデックススキャンを抑制し、テーブルアクセスへ誘導することもできる。
—
実践:設定を変える前にやるべきこと
ここで一つ、先輩として釘を刺しておきたい。
「いきなり本番環境でこれらをいじるのは絶対にやめてくれ」。
コスト定数の変更は、影響範囲がデータベース全体に及ぶ。あるクエリが速くなった裏で、別の重要なバッチ処理が激遅になる……なんていうのは「あるある」だ。
まずは検証環境で、`SET`コマンドを使ってセッション単位でシミュレーションするのが鉄則だよ。
— 検証用クエリの実行計画を確認
EXPLAIN ANALYZE SELECT FROM users WHERE status = ‘active’;
— 一時的にコスト定数を変更して、プランの変化を見る
SET cpu_tuple_cost = 0.05;
— 再度実行計画を確認
EXPLAIN ANALYZE SELECT FROM users WHERE status = ‘active’;
もしこれで狙った実行計画(インデックススキャンなど)が選ばれるようになったら、初めてパラメータ変更を検討するステージに立てたと言える。
—
最後に:定数調整は「最後の手段」
現場で長く働いていると、「コスト定数をいじれば魔法のように速くなる」と勘違いしているエンジニアによく出会う。でも、実際はそんなことないんだ。
プランナが変な計画を立てる最大の原因は、実は「統計情報の不一致」であることが9割だ。まずは`ANALYZE`をかけて、テーブルの行数やデータの偏りが正確にプランナに伝わっているかを確認しよう。
それでも解決しない、特定のインデックスがどうしても使われない……そんな「どうしようもない状況」の時に初めて、このコスト定数という「スパイス」を微調整する。これが、プロのチューニングだよ。
もし設定を変えるなら、変更前と変更後の実行計画を必ずログに残して、経過観察を忘れないようにね。
また何か詰まったら、いつでも聞きに来てくれ。一緒にクエリの裏側を覗いていこうぜ。
コメント