【実務・中級編】 シーケンシャルスキャン – PostgreSQL

「Seq Scan」は悪者じゃない。PostgreSQLの性能を握る「読み」の極意

やあ。今日もクエリの実行計画(EXPLAIN)と睨めっこしてるかな?

現場でコードレビューをしていると、「Seq Scan(シーケンシャルスキャン)が出てる! インデックスが効いてないからダメだ!」と慌ててインデックスを貼りまくる若手をよく見かけるんだ。

でもね、ちょっと待ってほしい。Seq Scanは決して「悪」じゃない。 むしろ、何も考えずにインデックスを貼りまくると、かえってパフォーマンスが落ちることもある。今日は、そんなPostgreSQLの基本中の基本、「シーケンシャルスキャン」の正しい向き合い方について話そうと思う。

—

シーケンシャルスキャンとは何か?

簡単に言えば、テーブルのデータを先頭から末尾まで、文字通り「全部舐める」アクセス手法だ。

本棚にある100冊の本の中から特定の1冊を探すとき、インデックス(索引)を使わずに1冊ずつ背表紙を確認していくようなものだね。当然、本が1000冊あれば時間がかかる。これがSeq Scanのイメージだ。

なぜ「あえて」Seq Scanが選ばれるのか?

PostgreSQLのオプティマイザは非常に賢い。「インデックスがあるなら使えばいいじゃないか」と思うかもしれないが、実はそうとも限らないんだ。

  • テーブルが小さい場合:

データ量が数十行程度の小さなテーブルなら、インデックスを読みに行くオーバーヘッドの方が高くつく。「全部メモリに載せて舐めちゃったほうが速い」という判断だね。

  • 全件取得・集計が必要な場合:

`SELECT COUNT() FROM users` のようにテーブル全体を見なければならない時や、テーブルの大部分(例えば7割以上)の行を抽出する時、PostgreSQLは迷わずSeq Scanを選ぶ。インデックス経由でランダムアクセスを繰り返すよりも、物理的に隣接したブロックをシーケンシャルに読む方が圧倒的に速いからだ。

実践:こんな時は「放置」でいい

例えば、こんな状況を想像してみてほしい。

— ユーザーのステータスが「有効」な人を数える
SELECT count() FROM users WHERE status = ‘active’;

もし、全ユーザーの90%が「active」だとしたら? インデックスを引いて行ポインタを拾い、実データを読みに行く(ランダムアクセス)を繰り返すより、テーブル全体をガッとメモリに読み込んだほうがI/O効率は格段にいい。

この時、`EXPLAIN` を見て「Seq Scanが出た!」と焦ってインデックスを貼る必要はないんだ。むしろ、「オプティマイザが賢明な判断を下している」と安心すべき場面だね。

逆に、「注意が必要な」Seq Scanとは?

問題になるのは、「本来はインデックスが効くはずのクエリなのに、Seq Scanが選ばれている場合」だ。これにはいくつか理由がある。

1. 統計情報が古い: `ANALYZE` を実行して、テーブルの分布状況を最新にしてあげよう。
2. データ型が一致していない: `WHERE` 句で文字列のカラムに数値を渡していないかな? 暗黙の型変換が発生すると、インデックスは無効になる。
3. インデックスが「刺さっていない」: `LIKE ‘%keyword%’` のような前方一致ではない検索や、関数を通した検索(`WHERE lower(name) = ‘…’`)などだね。

先輩からのアドバイス:チューニングの心構え

現場でパフォーマンスの問題にぶつかったら、まずは以下の手順で考えてみてほしい。

1. `EXPLAIN (ANALYZE, BUFFERS)` を叩く:
実行計画だけでなく、実際にどのくらいブロックを読んでいるかを見る。
2. 「本当に遅いのか?」を疑う:
Seq Scanしていても、実行時間がミリ秒単位なら、それはチューニングの対象外だ。アプリケーション全体のボトルネックは別のところにあるはず。
3. インデックスは「諸刃の剣」:
インデックスを増やせば、書き込み(INSERT/UPDATE)が重くなる。読み込みを速くするために書き込みを殺しては本末転倒だ。

—

結論として、Seq Scanは「PostgreSQLが気を利かせた結果」であることが多い。

敵視するんじゃなくて、まずは「なぜPostgreSQLはあえてこれをやっているのか?」という視点で実行計画を読み解いてみてほしい。そうすれば、インデックスを貼るべき場所と、そっとしておくべき場所が、自然と見えてくるはずだよ。

さて、コーヒーでも飲んで、もう一度ログを眺めてみようか。何か困ったことがあったら、いつでも聞いてくれよな。

コメント

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