【実務・中級編】 インデックススキャンとシーケンシャルスキャンの選択基準 – PostgreSQL

なぜPostgreSQLは「正しいはずのインデックス」を無視するのか?クエリチューニングの深淵へ

現場でPostgreSQLを触っていると、たまにこんな場面に出くわさないかな。「このカラムにはちゃんとインデックスを貼ったのに、なんで実行計画(EXPLAIN)を見るとシーケンシャルスキャン(Seq Scan)してるんだ?」って。

最初は「バグかな?」なんて思うかもしれないけれど、実はこれ、PostgreSQLのオプティマイザが「あえて」そう判断している可能性が高いんだ。今日は、プランナがどうやってスキャン方法を選んでいるのか、その裏側にある「コスト」という名の判断基準について、少し深掘りしてみよう。

—

1. プランナは「ケチ」であるべきだ

まず大前提として、PostgreSQLのプランナは、「いかにディスクI/Oを減らすか」という一点において、とことんケチなんだ。

インデックススキャンは、一見すると万能に見えるよね。でも、ランダムアクセスが発生する分、データが大量にある場合は実はコストが高い。一方でシーケンシャルスキャンは、データブロックを端から順番に読み込むシーケンシャルアクセスだから、ストレージからすれば「連続読み込み」として非常に効率がいい。

プランナは、クエリが実行される前に統計情報を元にこう考えているんだ。

  • 「インデックスを使って数万回ランダムアクセスするコスト」 vs 「テーブル全体を端からスキャンするコスト」

この比較の結果、後者の方が安い(速い)と判断すれば、容赦なくインデックスを無視してSeq Scanを選択する。これが、「インデックスを無視された」ように見える現象の正体だ。

2. 「閾値」はどこにあるのか?

じゃあ、どのくらいのデータ量でインデックススキャンからSeq Scanに切り替わるのか? 結論から言うと、「明確な固定の閾値はない」んだ。

テーブルのサイズ、カラムの選択率、物理的なデータの並び順、さらには `random_page_cost` の設定値など、複数の要因が絡み合ってコストが算出される。

例えば、`random_page_cost` がデフォルトの `4.0` だと、インデックススキャンはシーケンシャルスキャンよりも「ランダムアクセスは4倍コストがかかる」と計算される。もし君の環境のストレージが爆速のNVMe SSDなら、この値を `1.1` くらいまで下げてやると、プランナはインデックススキャンをより積極的に選ぶようになるはずだよ。

3. なぜ「統計情報」が狂うと悲劇が起きるのか

さて、ここからが現場で一番怖い話。プランナの判断はすべて「統計情報(`pg_stats`)」に基づいている。もしこの情報が実態と乖離していたらどうなるか?

想像してみてほしい。実際にはテーブルの90%が該当するようなクエリなのに、統計情報が古くて「該当するのは0.1%だけ」だと誤解していたら。

1. プランナ:「0.1%ならインデックススキャンの方が圧倒的に速いな!」と判断。
2. 実行:「……あれ? 予想と違ってめちゃくちゃデータがあるぞ。インデックスを辿って何度もディスクにアクセスしなきゃ……」
3. 結果:激遅。

これが、統計情報の更新(`ANALYZE`)を怠った時に発生する悲劇だ。

実践的なデバッグ手順

もしインデックスが使われなくて悩んだら、まずは以下の手順で切り分けてみて。

1. `EXPLAIN ANALYZE` を取る
まず実際の実行計画と、推測された行数(rows)と実際の行数(actual rows)を見比べること。「Estimated」と「Actual」で桁が違っていたら、それが犯人だ。

2. 統計情報を更新する

ANALYZE テーブル名;

これで解決するなら、単に統計が古かっただけ。

3. `random_page_cost` を疑う
統計が正確なのにSeq Scanされるなら、プランナが「インデックスはコストが高い」と思い込んでいる。

— 現在のコスト設定を確認
SHOW random_page_cost;

SSD環境なら `SET random_page_cost = 1.1;` などで様子を見るのも一つの手だね(ただし、セッション単位か設定ファイルで慎重にやるように)。

最後に:ツールに踊らされるな

最後に一つだけアドバイスを。「Seq Scan=悪」という思い込みは捨ててほしい。

テーブルの大部分を読み出す処理なら、むしろSeq Scanの方が圧倒的に速い。インデックスは「必要なデータを絞り込むための道具」であって、万能薬じゃないんだ。

プランナは我々よりもずっと賢く、そしてシビアにコストを計算している。彼らの判断が間違っている時は、たいてい我々が提供している「統計という名の地図」が間違っている時なんだよ。

まずは `EXPLAIN ANALYZE` を友達にして、PostgreSQLが「なぜそう判断したのか」の思考をトレースする癖をつけてみて。それがチューニングの第一歩だよ。

それじゃ、また現場で!

コメント

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