「なぜか速い」の正体を知る:PostgreSQLの隠れた立役者「Visibility Map」の話
PostgreSQLを触っていると、「Index Only Scan(インデックスオンリースキャン)」という言葉をよく耳にするよね。普通のIndex Scanが「インデックスで場所を探して、テーブル本体(ヒープ)を見に行く」のに対し、Index Only Scanは「インデックスだけで完結させる」。
これ、クエリのレスポンスが段違いに速くなる魔法のような仕組みなんだけど、実はその裏で「Visibility Map(可視性マップ)」という地味だけど極めて優秀なやつが働いているんだ。
今日は、このVisibility Mapが実際に何をしていて、どうすればこいつを味方につけられるのか、現場の視点で解説していくよ。
—
Visibility Mapって結局なんなの?
一言で言うと、「そのページ(ブロック)内のデータは、誰から見ても最新で、変更の必要がないよ」ということを記録したビットマップだ。
PostgreSQLはMVCC(多版同時実行制御)を採用しているから、クエリが実行されるたびに「この行は今のトランザクションから見て有効かな?」と、ヒープまでわざわざデータを見に行かなきゃいけない。これがI/O負荷の正体なんだけど、Visibility Mapがあれば話は別だ。
「このページのタプルは、全トランザクションから見て確実に可視(Visible)だ!」とマップが教えてくれれば、わざわざ重いヒープを読みに行かなくても、インデックスの情報だけで「はい、正解」って判断できる。これがIndex Only Scanを爆速にする秘密というわけ。
実際にVisibility Mapが効いているか確認してみよう
理屈だけじゃピンとこないと思うから、実際に手元のPostgreSQLで試してみよう。まずは適当なテーブルを作って、中身を詰め込んでみる。
— テスト用テーブル作成
CREATE TABLE users (id serial primary key, name text);
INSERT INTO users (name) SELECT ‘user_’ || i FROM generate_series(1, 100000) i;
— インデックス作成
CREATE INDEX idx_users_name ON users(name);
— 統計情報を更新してプランナに教える
VACUUM ANALYZE users;
ここで、名前を検索するクエリを投げてみるよ。
EXPLAIN (ANALYZE, BUFFERS)
SELECT name FROM users WHERE name = ‘user_99999’;
実行結果を見ると、最初は `Index Scan` になることが多いはずだ。なぜなら、PostgreSQLは「このデータが本当に正しいか(他で更新されてないか)」をヒープまで確認しに行く必要があるから。
ここで一度、`VACUUM` を実行してみよう。
VACUUM users;
もう一度同じ `EXPLAIN` を叩いてみて。今度は `Index Only Scan` に変わったはずだ。これがVisibility Mapが「もうこのデータは誰からも見えるよ」と確約してくれた瞬間だね。
実務で「Index Only Scan」を狙うための鉄則
ただ、現場では「インデックスを貼ったのにIndex Only Scanにならない!」という相談をよく受ける。原因はだいたい決まってるんだ。
1. データが更新され続けている
データが頻繁に `UPDATE` されると、Visibility Mapのビットはすぐにオフ(無効)にされる。こうなるとPostgreSQLはヒープを見に行かざるを得ない。書き込みが激しいテーブルでIndex Only Scanを期待するのは少し酷だよ。
2. VACUUMのタイミング
Visibility Mapは `VACUUM` が走った時に更新される。`autovacuum` の設定が甘くてなかなか掃除が始まらないと、いつまでもIndex Only Scanにならない。ログを確認して、VACUUMがちゃんと追いついているかチェックするのは基本中の基本だね。
3. ヒープへのアクセスが必要な列が含まれている
`SELECT ` なんて書いたらダメだよ。インデックスに含まれていない列を要求すれば、強制的にヒープを参照することになるからね。
先輩からのアドバイス
Visibility Mapを意識し始めると、クエリのチューニングが一段階上のレベルに行ける。
「インデックスを貼ったのに遅い」と悩んだら、`EXPLAIN ANALYZE` を叩いて、`Heap Fetches` という項目を見てみてほしい。この値が0に近いなら、Visibility Mapが完璧に働いている証拠だ。逆にここが大きいと、インデックスを読みに行った後、無駄にヒープへアクセスしていることになる。
データベースの世界は、結局のところ「いかに不要なI/Oを削るか」というゲームなんだ。Visibility Mapはそのための強力な武器だよ。
「理論を知って、計測して、チューニングする」。このサイクルを回せるようになれば、君ももう立派なPostgreSQL使いだ。また何か躓いたら、いつでも聞いてくれよな。
コメント