【実務・中級編】 クエリプランナーのコストパラメータ – PostgreSQL

「PostgreSQLのクエリがなぜか遅い……インデックスも貼っているのに、なんでフルスキャン(Seq Scan)してるんだ?」

現場でこんな悲鳴を聞くと、僕は決まって「プランナーの設定値、ちゃんと見てる?」と聞くことにしているんだ。

PostgreSQLのオプティマイザは非常に賢いけれど、「何をもってコストが高いとみなすか」という価値観は、デフォルトのままだと現代のハードウェア環境とズレていることがよくある。今日は、クエリプランナーの裏側を覗いて、君のデータベースを「今の環境」に最適化させるための話をしよう。

—

なぜ「コストパラメータ」をいじる必要があるのか?

PostgreSQLのプランナーは、実行計画を立てる際に「このクエリを実行するのにどれくらいのコストがかかるか」を数値化して比較している。その計算式で使われているのが、`postgresql.conf` にある一連のコストパラメータだ。

特に重要なのが、ストレージへのアクセスに関するこの二つだ。

  • `seq_page_cost`: シーケンシャルアクセスで1ページ読み込むコスト(デフォルト: 1.0)
  • `random_page_cost`: ランダムアクセスで1ページ読み込むコスト(デフォルト: 4.0)

見ての通り、デフォルト設定では「ランダムアクセスはシーケンシャルの4倍コストがかかる」という前提になっている。これはHDD(ハードディスク)全盛時代の名残だ。物理的なヘッドの移動が必要なHDDなら、ランダムアクセスは確かに重い。

でも、今のインフラがSSDやNVMeならどうだろう?
読み込みのコスト差は、HDDほど大きくないはずだよね。ここがズレていると、プランナーは「インデックスを使うより、いっそ全部読み込んだ方が速いんじゃね?」と勘違いして、無駄なフルスキャンを選んでしまうことがある。

—

実践:どうやってチューニングするか

まず、今の設定を確認してみよう。

SHOW seq_page_cost;
SHOW random_page_cost;

もし君の環境がクラウドの高速なSSDストレージなら、`random_page_cost` を 1.1 〜 1.2 くらいまで下げてみるのが定石だ。

設定変更のステップ

まずはセッション単位で試すのがエンジニアの作法だね。

— セッション単位で変更してクエリの挙動を確認
SET random_page_cost = 1.1;

EXPLAIN ANALYZE SELECT FROM users WHERE id = 12345;

これで計画が「Seq Scan」から「Index Scan」に変わるなら、プランナーが本来のハードウェア性能を理解できていなかった証拠だ。

—

もう一つの隠れた主役:cpu_tuple_cost

忘れられがちだけど、めちゃくちゃ効いてくるのが `cpu_tuple_cost` だ。これは「1行(タプル)を処理するコスト」なんだけど、これが大きすぎると、プランナーは「行数が多いテーブルをスキャンするのは避けよう」と、極端にインデックスを好むようになる。

逆に、複雑なJOINや集計を多用する分析系クエリが多い環境では、ここを少し調整することでプランナーの判断が劇的に改善することがある。

  • `cpu_tuple_cost` (デフォルト: 0.01)
  • メモリ上で複雑な計算やフィルタリングをたくさんする場合、ここをいじると「インデックスを貼ったけど、結局全件スキャンしたほうが速いよね」というプランナーの妥当な判断を引き出しやすくなる。

—

先輩からのアドバイス:闇雲にいじらないこと

ここまで読んで「じゃあ全部下げればいいんだな!」と思った君、ちょっと待って。

コストパラメータは「バランス」が全てだ。
特定のクエリを速くしようとして極端な値を設定すると、別の重要なクエリでプランナーが迷走し、システム全体が崩壊するリスクがある。

1. まずは `EXPLAIN (ANALYZE, BUFFERS)` を見る: どこで時間がかかっているのか、本当にインデックスが使われていないのかを確信すること。
2. `random_page_cost` から着手する: SSD環境なら、まずはここを 1.1 に寄せるだけで十分な効果が出ることが多い。
3. 統計情報を疑う: プランナーが誤る最大の原因は、実はパラメータではなく「古い統計情報」であることが大半だ。「`ANALYZE;` を打ったら直った」というのは、現場あるある中のあるあるだよ。

—

最後に

データベースエンジニアの仕事は、魔法をかけることじゃなくて、プランナーに「今のハードウェアはこれくらい優秀なんだよ」という正しい現実を教えてあげることだ。

設定を変更したら、必ず本番に近いデータ量と負荷で検証してほしい。もし「設定を変えたのに変わらない!」という時は、設定値の反映漏れ(再起動やセッションの張り直し)や、`autovacuum` が追いついていない可能性を疑ってみて。

また何か詰まったら聞きに来てくれ。データベースの深淵を一緒に攻略しようじゃないか。

コメント

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