「そのSeq Scan、本当に悪者ですか?」――seq_page_costを巡るチューニングの深淵
PostgreSQLのクエリチューニングにおいて、`seq_page_cost`というパラメータは、まるで「使いこなせば名刀だが、扱いを誤れば自らを切り裂く諸刃の剣」のような存在です。
多くのエンジニアは「Index Scanの方が速いから、Seq Scanを抑制するためにコストを上げよう」と考えがちです。しかし、PostgreSQLのオプティマイザ(コストベース・オプティマイザ)の深層心理――つまり、データが物理ディスクやOSのページキャッシュにどう配置され、どう読み込まれるかという「アーキテクチャの呼吸」を理解せずにこの数値をいじると、待っているのは地獄のようなパフォーマンスの劣化です。
今日は、この`seq_page_cost`が単なる数字の羅列ではないことを、現場の視点から紐解いていきましょう。
—
なぜデフォルト値は「1.0」なのか
PostgreSQLのデフォルト設定では、`seq_page_cost`は`1.0`、`random_page_cost`は`4.0`に設定されています。これは「ランダムアクセスはシーケンシャルアクセスに比べて4倍のコストがかかる」という前提に基づいています。
しかし、現代のインフラはどうでしょうか?
NVMe SSDのような高速なストレージ環境では、ランダムアクセスとシーケンシャルアクセスの差は、HDD時代ほど劇的ではありません。にもかかわらず、ここを不用意に弄ってしまうと、オプティマイザは以下のような「誤解」をし始めます。
- Seq Scanへの偏愛: `seq_page_cost`を下げすぎると、オプティマイザは「テーブル全体を舐めるほうが、インデックスを飛び回るより効率的だ」と錯覚します。
- Index Scanの忌避: 本当はインデックスを使ったほうがトータルのI/Oが少ないケースでも、オプティマイザがそれを「コストが高い」と判断し、フルスキャンを選択してしまいます。
パフォーマンストラブルの現場で何を見るべきか
私がパフォーマンスチューニングの現場で最初に確認するのは、`EXPLAIN ANALYZE`の出力結果と、実際のディスクI/Oの挙動の乖離です。
例えば、メモリに乗るはずのデータ量なのに、なぜかSeq Scanが選択され、クエリが停滞しているケース。あるいは、逆にIndex Scanが選択され、ランダムI/Oが爆発してレイテンシが跳ね上がっているケース。
ここで`seq_page_cost`を調整する前に、まずは以下の2点を確認してください。
1. `effective_cache_size`は適切か?
これが低すぎると、オプティマイザは「メモリに乗っているページ」を過小評価し、ディスクI/Oを前提としたコスト計算を行います。
2. `random_page_cost`との比率
極端なストレージ構成(例えば、超高速なNVMeと、巨大なアーカイブ用HDDが混在しているような環境)でない限り、`random_page_cost`を`1.1`程度まで下げてみるのが現代のトレンドです。`seq_page_cost`自体をいじるのは、そのあとの「最後の手段」であるべきです。
「コスト」をいじるという行為の重み
私が経験した苦い事例の一つに、ある大規模なテーブルのクエリが重くなった際、焦って`seq_page_cost`を極端に下げたエンジニアがいました。結果どうなったか。オプティマイザは全クエリでSeq Scanを優先するようになり、本来数ミリ秒で終わるはずのインデックス探索が、数秒のフルスキャンに置き換わってシステム全体が沈没しました。
コスト設定を変更する際は、以下のルールを自分に課してください。
- グローバル設定には触らない: 特定のテーブルや関数だけで問題が起きているなら、`ALTER TABLE … SET (seq_page_cost = 0.5)`のように、対象を限定して適用すること。
- ベースラインを計測する: 変更前後の「実行計画の変遷」と「実際にクエリが消費したバッファ(Shared Hit/Read)」を必ず数値で比較すること。
- 「なぜそのスキャン手法が選ばれたのか」を言語化する: 「なんとなく速くなりそうだから」ではなく、「このインデックスの相関性が低いために、Seq Scanの方が結果的にI/Oが少なく済むはずだ」という明確なロジックが必要です。
最後に
PostgreSQLのオプティマイザは、非常に賢い統計学者です。彼らがSeq Scanを選択したのには、それなりの理由があります。もし彼らの判断が間違っているように見えるなら、それは`seq_page_cost`が悪いのではなく、統計情報(`ANALYZE`)が古いか、あるいは我々がストレージの特性を正しく設定ファイルに伝えられていないだけかもしれません。
チューニングとは、データベースを「ねじ伏せる」作業ではありません。データベースが持つ本来のポテンシャルを引き出せるよう、環境という「文脈」を正しく教えてあげる作業なのです。
皆さんのクエリが、今日もインデックスの木々を軽やかに飛び回ることを願っています。
コメント