「全件スキャンが遅い!」と嘆く前に。PostgreSQLの `seq_page_cost` を触るべき本当のタイミング
どうも。最近、若手から「クエリの実行計画がどうしてもSeq Scan(シーケンシャルスキャン)になっちゃって遅いんです」という相談を受けることが増えました。
PostgreSQLのオプティマイザは非常に優秀ですが、たまに「いや、そこはインデックス使おうよ!」という場面で頑なに全件スキャンを選んでしまうことがありますよね。そんなとき、多くのエンジニアがインデックスの定義を見直したり、`ANALYZE` をかけて統計情報を更新したりしますが……それでも解決しないとき。
今回は、PostgreSQLのコストモデルの根幹である `seq_page_cost` にスポットを当てて、いざという時の「チューニングの切り札」についてお話ししようと思います。
—
そもそも `seq_page_cost` って何者?
PostgreSQLには、クエリの実行計画を立てる際、「どの手法が一番コストが低いか」を計算するためのパラメータがいくつかあります。その中で、ディスク上のデータページを順番に読み込む(シーケンシャルアクセス)際のコスト係数を決めているのが `seq_page_cost` です。
デフォルト値は `1.0`。
つまり、PostgreSQLは「シーケンシャルスキャンで1ページ読むコストを1」と基準にして、他の操作(ランダムアクセスなど)のコストを相対的に計算しているんです。
- `seq_page_cost`: シーケンシャルスキャンのコスト(デフォルト 1.0)
- `random_page_cost`: ランダムアクセスのコスト(デフォルト 4.0)
この比率が、オプティマイザの「インデックスを使うか、全件スキャンするか」の判断を左右する境界線になっています。
—
なぜデフォルト値を変える必要があるのか?
最近のサーバー環境を想像してみてください。NVMe SSDのような爆速なストレージを使っているのに、`random_page_cost` が `4.0` のままだと、オプティマイザは「ランダムアクセスは高いから、SSDでもインデックスより全件スキャンの方が安上がりだよね」と誤解してしまうことがあります。
逆に、`seq_page_cost` を調整したい場面というのは、「特定のテーブルだけ、全件スキャンがどうも重すぎて、もっとインデックスを積極的に使ってほしい」というような、実務上の「偏り」を補正したいときです。
—
具体的なチューニングの実践
いきなり全体設定(`postgresql.conf`)をいじるのは危険です。まずは特定のセッションやテーブルに対して試すのが、現場の鉄則ですよ。
例えば、ある巨大なテーブルで、特定のクエリが「本当はインデックスを使ってほしいのに、Seq Scanが選ばれる」という状況だとします。
— まずは現在のコストを確認してみる
EXPLAIN ANALYZE SELECT FROM users WHERE status = ‘active’;
もしここでインデックスが使われていないなら、実験的にコスト係数を操作してみます。
1. セッション単位で試す(検証用)
BEGIN;
SET LOCAL seq_page_cost = 2.0; — シーケンシャルスキャンのコストをあえて高く見積もらせる
EXPLAIN ANALYZE SELECT FROM users WHERE status = ‘active’;
— これでインデックスが選ばれるようになるか確認!
ROLLBACK;
`seq_page_cost` をデフォルトの `1.0` から `2.0` に引き上げることで、PostgreSQLは「全件スキャンって、実はけっこうコスト高いんだな」と認識を改め、インデックススキャンへ誘導されやすくなります。
2. テーブル単位で最適化する(恒久的な対処)
もし特定のテーブルが常にこの挙動で困っているなら、テーブル単位で設定を上書きすることも可能です。
ALTER TABLE users SET (seq_page_cost = 2.0);
これなら、データベース全体の設定を汚さずに、問題のある箇所だけピンポイントで調整できます。これができるのがPostgreSQLの懐の深いところですね。
—
注意点:魔法の杖ではない
最後に、これだけは覚えておいてください。コストパラメータの変更は「最後の手段」です。
- まずはインデックスが適切か?
- `ANALYZE` で統計情報は最新か?
- クエリの書き方に無駄はないか?
これらをすべて確認した上で、それでもなお「物理構成上、明らかに全件スキャンがボトルネックになっている」という確信がある場合のみ触ってください。闇雲に触ると、別のクエリで「本来インデックスを使うべきじゃない場面でインデックスを使ってしまい、逆に遅くなる」という地獄を見ることになります。
まとめ
`seq_page_cost` を触るということは、PostgreSQLの「脳内偏差値」を調整するようなものです。
最初は怖いかもしれませんが、`EXPLAIN` の結果とじっくり向き合いながら、「なぜオプティマイザはこう判断したのか?」を読み解く力は、間違いなくあなたのエンジニアとしての武器になります。
「設定値を少しいじって、クエリが劇的に速くなったときの快感」。これがあるからデータベースエンジニアは辞められないんですよね。皆さんの現場でも、困ったときはぜひこのアプローチを試してみてください。
それでは、また次回の記事で!
コメント