PostgreSQLの「隠れた功労者」、可視性マップ(Visibility Map)を理解してクエリを爆速にする話
やあ。最近、PostgreSQLのパフォーマンスチューニングで頭を抱えていないか?
「インデックスを貼ったのに、なぜかスキャン速度が上がらない……」とか、「IO負荷が異様に高いんだよな」なんて悩み、一度は経験するよな。
今日は、そんな悩みを解決するかもしれない、PostgreSQLの「縁の下の力持ち」、可視性マップ(Visibility Map)について話をしよう。
教科書的な定義は「各ページ内のタプルがすべてのトランザクションから可視であるかを示すビットマップ」なんだけど、これだけだと少しピンとこないだろ? 実践的な視点で紐解いていくぞ。
—
なぜ「可視性マップ」が重要なのか?
PostgreSQLはMVCC(多版同時実行制御)を採用しているから、あるデータが「今、誰から見えているか」を判定するために、いちいちテーブルのデータページ(ヒープ)を見に行かなきゃいけない。
普通、インデックスのみスキャン(Index Only Scan)をするとき、PostgreSQLはインデックスを確認したあと、「本当にそのデータが今も有効か?」を確認するために、ヒープページまで読み込みに行くんだ。これを「ヒープフェッチ」と呼ぶ。
ここで登場するのが可視性マップだ。
可視性マップは、特定のページ内のすべてのタプルが「すべてのトランザクションから可視(つまり、削除も更新もされていない、いわゆる『全可視』の状態)」であることをビットで保持している。
もしこのビットが立っていれば、PostgreSQLは「このページ内のデータは誰から見ても有効だ」と確信できるから、わざわざヒープページを見に行かなくて済むんだ。これが「Index Only Scan」の真の力だよ。
—
具体的な挙動を見てみよう
百聞は一見に如かず。実際にどう動いているか、`EXPLAIN (ANALYZE, BUFFERS)` を使って確認するのが一番だ。
— サンプルテーブル作成
CREATE TABLE users (id serial PRIMARY KEY, name text);
INSERT INTO users (name) SELECT ‘user’ || i FROM generate_series(1, 100000) i;
— 統計情報を更新して可視性マップを構築
VACUUM ANALYZE users;
— さあ、確認だ
EXPLAIN (ANALYZE, BUFFERS)
SELECT id FROM users WHERE id < 100;
このとき、実行計画に `Heap Fetches: 0` と出ていれば大成功だ。可視性マップのおかげで、インデックスだけでクエリが完結している証拠だな。
---
現場で気をつけるべき「罠」
この可視性マップ、実はかなりデリケートなんだ。以下の状況に陥ると、途端に機能しなくなる。
1. 頻繁な更新(UPDATE/DELETE):
データが更新されると、そのページの可視性マップは「全可視ではない」とマークされる。すると、次のクエリは再びヒープページを見に行く必要がある。更新頻度が高いテーブルでは、Index Only Scanの効果は薄れるんだ。
2. VACUUMの不足:
可視性マップは `VACUUM` が実行されるタイミングで更新される。つまり、`autovacuum` が適切に走っていないと、古い情報のまま放置され、結局ヒープへアクセスし続けることになる。
3. トランザクションの長時間滞留:
古いトランザクションが残っていると、PostgreSQLは「このデータ、まだ誰かに見える必要があるかも」と判断して、可視性マップを正しく更新できないことがある。
—
先輩からのアドバイス:どう向き合うか?
もし現場で「Index Only Scanが効いていないな」と感じたら、まずは以下のチェックリストを確認してくれ。
- `pg_stat_user_tables` の `n_tup_upd` や `n_tup_del` を見て、更新が激しすぎないか確認する
- `autovacuum` が適切に走っているか確認する
- 長時間走っているトランザクションがないか `pg_stat_activity` で監視する
可視性マップは、目に見えないところでPostgreSQLが頑張っている証拠だ。これを意識するだけで、インデックス設計のレベルが一つ上がるはずだよ。
PostgreSQLは、「なぜ遅いのか」を論理的に追いかけていけば、必ず答えが見つかるデータベースだ。この可視性マップのような「仕組み」を武器にして、ぜひ最高のパフォーマンスを引き出してやってくれ。
また何か詰まったら、いつでも聞きに来いよ。応援してるぜ!
コメント