【テクニカル・上級編】 シーケンシャルスキャン – PostgreSQL

シーケンシャルスキャンを「悪」と決めつける前に、もう一度だけ向き合ってみないか?

PostgreSQLのチューニングにおいて、`Seq Scan`(シーケンシャルスキャン)という単語が出た瞬間、反射的にインデックスを貼りたくなる気持ちはよくわかる。僕も駆け出しの頃は、`EXPLAIN`の結果に「Seq Scan」の文字が見えるだけで、まるでコードにバグを見つけたかのように修正に取り掛かっていたものだ。

だが、長年大規模なデータセットと格闘してくると、ある真理にたどり着く。「シーケンシャルスキャンは、決して『避けるべき敵』ではない」ということだ。むしろ、使い方次第では最強の武器になる。

今日は、そのあたりの「解像度」を少しだけ深掘りしてみようと思う。

—

なぜPostgreSQLは「あえて」全件走査を選ぶのか

まずアーキテクチャの話をしよう。PostgreSQLのクエリプランナがシーケンシャルスキャンを選択する理由は、単にインデックスがないからだけではない。

1. 「コスト」の冷徹な計算

PostgreSQLは、ランダムI/OよりもシーケンシャルI/Oの方が圧倒的に高速であるという前提で動いている。HDDやSSDの物理特性を考えれば当然だが、メモリ上のバッファキャッシュ(`shared_buffers`)のヒット率が低い場合、インデックスを辿ってランダムにデータをフェッチするよりも、テーブル全体をメモリに乗せて順次読み込む方が、結果的にスループットが高いと判断されるケースがある。

特に、テーブルのサイズが小さい場合、あるいは取得対象がテーブル全体(あるいは大部分)である場合、インデックスのオーバーヘッドを支払うよりも、頭から最後まで一気に読み飛ばす方が、エンジニアリングとして「正解」なのだ。

2. インデックスのオーバーヘッド

インデックスは魔法ではない。インデックスを辿るためのB-Treeのツリー走査コストと、そこからヒープ(テーブル本体)を参照する「テーブルアクセス」のコスト。この合計が、シーケンシャルスキャンよりも高くなると判断された瞬間、プランナは迷わず「全件走査」を選ぶ。

—

シーケンシャルスキャンが「トラブル」に変わる瞬間

しかし、もちろん問題になることはある。僕たちが現場で遭遇する「遅延」の多くは、単なるスキャンではなく、以下のような複合的な要因によるものだ。

  • 統計情報の陳腐化: `ANALYZE`が走っておらず、プランナがテーブルサイズを過小評価している場合。数千万行のテーブルを「数行しかない」と誤認してSeq Scanを選択し、数分間DBがフリーズする……これはよくある悲劇だ。
  • 不要なカラムのフェッチ: `SELECT ` で巨大なTEXT型やJSONBカラムを含んだまま全件走査すれば、I/O帯域は一瞬で食い尽くされる。
  • メモリ不足によるTemp Fileの発生: ソートやハッシュ結合が絡むSeq Scanの場合、`work_mem`を超過してディスクへの退避が発生していないか確認してほしい。これが起きると、シーケンシャルスキャンの優位性は完全に消し飛ぶ。

—

チューニングの現場で僕が確認するチェックリスト

もし君が今、「このSeq Scanのせいでクエリが遅い」と悩んでいるなら、まずはこれを確認してみてほしい。

1. `EXPLAIN (ANALYZE, BUFFERS)` を叩け:
`Buffers`オプションを付けて実行するんだ。`shared hit`と`read`の数を見れば、キャッシュが効いているのか、あるいは物理ディスクをどれだけ叩いているのかが一目瞭然だ。
2. 統計情報の鮮度を疑え:
`pg_stat_user_tables`を確認しよう。`last_analyze`の時刻が古くないか? もしそうなら、手動で`ANALYZE`をかけてプランが変わるか試してみる価値はある。
3. 「本当にインデックスが必要か?」を問い直す:
全件走査が遅いと感じている時、実は「抽出条件」の問題ではなく、「並び順(ORDER BY)」や「結合(JOIN)」の戦略がまずいことが多い。インデックスを作る前に、`EXPLAIN`のコスト値と実際の実行時間を照らし合わせ、本当にそれがボトルネックなのかを切り分ける冷静さが、熟練の証だ。

—

最後に:ツールとしての誇り

シーケンシャルスキャンは、データベースの原初的なアクセス手法だ。しかし、PostgreSQLのオプティマイザは、長年のアップデートを経て、この「泥臭い手法」を極限まで洗練させてきた。

だからこそ、安易にインデックスで解決しようとするのではなく、まずは「なぜデータベースがこの手段を選んだのか?」を、プランナの視点に立って考えてみてほしい。その先には、単なるパフォーマンス改善を超えた、PostgreSQLというエンジンの深淵が見えてくるはずだ。

さて、そろそろ次のクエリのプロファイリングに戻ろうか。君の環境で、最適な実行計画が見つかることを祈っている。

コメント

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