【実務・中級編】 コストパラメータのチューニング – PostgreSQL

なぜPostgreSQLの「見積もり」は時々的外れなのか?――コストパラメータ調整の深淵へ

現場でPostgreSQLをいじっていると、たまに「なんでこんな変な実行計画を選択するんだ?」と首を傾げたくなることはないだろうか。

インデックスは完璧に貼ってある。統計情報も最新だ。それなのに、オプティマイザはあえて遅いシーケンシャルスキャンを選んでしまう。

その原因の多くは、PostgreSQLが持っている「コストパラメータ」が、今のハードウェアの現実と乖離しているからかもしれない。今日は、データベースエンジニアの現場で避けては通れない、この「コスト調整」という黒魔術について話をしよう。

—

「コスト」とは何者か?

PostgreSQLのクエリプランナーは、実行可能な複数の計画に対してそれぞれ「コスト」を算出し、最も低いものを選ぶ。このコスト計算の根拠になっているのが、`postgresql.conf` にある一連のパラメータだ。

特に重要なのが、ディスクI/OとCPUの重み付けだ。

  • `seq_page_cost`:シーケンシャルスキャンでページを読み込むコスト(デフォルト:1.0)
  • `random_page_cost`:ランダムアクセスでページを読み込むコスト(デフォルト:4.0)
  • `cpu_tuple_cost`:1行(タプル)を処理するコスト(デフォルト:0.01)

デフォルト設定の歴史を辿ると、HDDがメインストレージだった時代の名残が色濃い。「HDDならランダムアクセスは遅いから、シーケンシャルアクセスの4倍のコストを課そう」という設計思想だ。

でも、今の現場はどうだろう? 多くのサーバーでNVMe SSDが当たり前になっている今、「ランダムアクセス=シーケンシャルアクセスの4倍遅い」という前提自体が、事実上の「嘘」になっているんだ。

—

SSD時代に合わせたチューニングの定石

もし君がAWSのEBS(特にio1やgp3)や、オンプレの高速なNVMe SSDを使っているなら、`random_page_cost` を見直すだけでクエリの挙動が劇的に改善することがある。

1. random_page_cost の引き下げ

SSDはランダムアクセスとシーケンシャルアクセスの速度差がほとんどない。だから、この値をデフォルトの `4.0` から `1.1` 程度まで下げてみるのが定石だ。

— 設定確認
SHOW random_page_cost;

— セッションレベルで一時的に変えて検証してみるのも手だ
SET random_page_cost = 1.1;
EXPLAIN ANALYZE SELECT FROM users WHERE id = 12345;

こうすることで、「インデックスを辿って飛び回る」という選択肢がオプティマイザにとって「安い」ものになり、インデックススキャンが積極的に選ばれるようになる。

2. seq_page_cost は動かさないのが吉

逆に `seq_page_cost` を触ることはあまりおすすめしない。これは基準点(ベースライン)になる値だからだ。ここをいじると計算のバランスが崩れやすく、思わぬところで実行計画が暴走するリスクがある。基本的には `1.0` のままにしておこう。

—

「CPUコスト」という伏兵

最近の高性能なCPUを使っていると、`cpu_tuple_cost` や `cpu_operator_cost` が相対的に重すぎて、本来インデックスを使ってサクッと終わるはずのクエリが、余計なコストを嫌ってフルスキャンに逃げてしまうことがある。

特に、数千万件あるテーブルで、条件分岐の多い複雑な検索をしているときにこの現象が起きやすい。もし「統計情報も正しい、インデックスも効くはずなのに、なぜかシーケンシャルスキャンになる」という場合は、試しに `cpu_tuple_cost` を少し下げてみる(例:`0.005` 程度)のも有効な手段だ。

—

チューニングをする前に必ず守ってほしい「鉄則」

最後に、一つだけ注意してほしい。これらの設定は「魔法の杖」ではないということだ。

1. まずは統計情報(ANALYZE)を疑え
コストパラメータを触る前に、テーブルの統計情報が古くなっていないか確認するのが鉄則だ。`pg_stats` を見て、ヒストグラムが正しく作成されているか確認しよう。

2. 全体設定をいきなり変えない
`postgresql.conf` を書き換えてサーバー全体に影響を出すのは最終手段だ。まずは `ALTER TABLE … SET (random_page_cost = 1.1)` のようにテーブル単位で調整するか、特定のトランザクション内だけで変更して、影響を検証する癖をつけてほしい。

3. 「直感」ではなく「プランナー」を信じる
「自分ならこうクエリを投げる」という直感と、オプティマイザの判断はしばしば異なる。`EXPLAIN (ANALYZE, BUFFERS)` を取って、実際にどこでコストがかかっているのか、実際のデータ量と見積もりにどれくらいの乖離があるのかを数字で追いかけること。

—

終わりに

データベースのチューニングは、料理に似ている。素材(ハードウェア)の特性を知り、隠し味(パラメータ)を少し加えることで、味が劇的に変わる。

「PostgreSQLは賢いからデフォルトでいい」なんて言うのは、初心者の証だ。ハードウェアの進化と、君たちが扱っているデータの性質に合わせて、パラメータを微調整する。その積み重ねが、数ミリ秒のレスポンス向上を生み、ユーザー体験を劇的に変えるんだ。

まずは手元の開発環境で、`random_page_cost` をいじって実行計画の変化を眺めてみてほしい。きっと、今まで見えていなかったPostgreSQLの「意思」が見えてくるはずだよ。

それじゃ、また現場で会おう。

コメント

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