PostgreSQLの「空き領域マップ(FSM)」:なぜPostgreSQLは挿入場所を即座に見つけられるのか?
やあ。データベースのパフォーマンスチューニングで悩んだことはあるかい?
「インデックスは完璧に貼ったはずなのに、なぜかINSERTが遅い」「バルクインサートで妙な詰まり方をする」。そんな時、PostgreSQLの内部で何が起きているのかを知っていると、トラブルシューティングの景色がガラッと変わる。
今日は、PostgreSQLの縁の下の力持ち、「空き領域マップ(Free Space Map:FSM)」について話そうか。教科書的な定義じゃなくて、現場でエンジニアが意識しておくべき「リアルな話」としてね。
—
FSMは「空き地の地図」だ
PostgreSQLで新しいデータ(タプル)を挿入するとき、DBはどこに書き込むか決めなきゃいけない。もし、すべてのページを頭から順番にスキャンして「ここなら入るかな?」なんて確認していたら、I/Oがいくらあっても足りないよね。
そこで登場するのがFSMだ。
FSMは、テーブルやインデックスの各ページに「あとどれくらい空き容量があるか」を保持している地図のようなものさ。これがあるおかげで、PostgreSQLは「このページなら入る!」という場所を、ディスクを全探索することなく一発で見つけられる。
FSMの構造を少し覗いてみよう
FSMは各テーブル・インデックスごとに作成される専用のファイル(`_fsm`という拡張子が付くやつだ)に保存されている。中身は「階層構造のツリー」になっているんだ。
- リーフノード: 各ページごとの具体的な空き容量をバイト単位(の近似値)で持っている。
- 上位ノード: 配下にあるページの中で「最も空きがある場所」の情報を保持している。
これのおかげで、INSERTのとき、PostgreSQLはツリーを辿るだけで「空き容量が十分なページ」に高速に到達できるわけだ。賢い仕組みだと思わないかい?
—
実務で知っておくべき「FSMの罠」
じゃあ、このFSM、現場ではどう意識すればいいのか。結論から言うと、「FSMの枯渇」と「更新の激しいテーブル」の関係を知っておく必要がある。
1. FSMがいっぱいになるとどうなる?
FSMの容量には上限がある。もしFSMが「空きはない」と誤認したり、あるいは本当に空きがなくなると、PostgreSQLは新しくページを割り当てる(テーブルの末尾を伸長する)ことになる。
これが何を引き起こすかというと、「テーブルの肥大化」だ。本来なら再利用できたはずのページが見落とされ、ディスク容量がどんどん消費されていく。
2. `vacuum` との密接な関係
FSMは主に `VACUUM` が実行されるときに更新される。`VACUUM` が走らないと、たとえ `DELETE` や `UPDATE` でデータが消えても、FSMは「そこには空きがあるぞ」と認識してくれない。
だからこそ、PostgreSQLの運用では「適切な `autovacuum` 設定」が命なんだ。`autovacuum` のチューニングが甘いと、FSMが最新状態に保たれず、結果としてINSERTのパフォーマンスが落ちる。これが「現場あるある」の典型だね。
—
ちょっとだけコード的な視点
PostgreSQLのソースコードを追いかけると、`GetPageWithFreeSpace` という関数に行き着くはずだ。興味があれば見てみるといい。
/ 概念的なロジック /
BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded)
{
/
- FSMツリーを辿って、spaceNeededを確保できるページを探す。
- 見つからなければ、新しいページを割り当てる(RelationExtensionLock)。
/
}
この処理が走っている間、ロックの競合が起きることもある。特に巨大なテーブルで頻繁にINSERT/UPDATEが走る場合、FSMの更新自体がボトルネックになることさえあるんだ。
—
先輩からのアドバイス
もし君が「INSERTが遅い」と感じているなら、まずは以下のチェックリストを試してみてくれ。
1. `pg_stat_user_tables` を見る: `n_dead_tup`(死んだタプル)が溜まっていないか? `last_autovacuum` はいつ実行されたか?
2. `pg_freespacemap` 拡張を使う: これを使うと、実際にどのページにどれくらいの空きがあるか、SQLで可視化できる。
CREATE EXTENSION pg_freespacemap;
SELECT FROM pg_freespace(‘your_table_name’);
これで、テーブルのどのあたりがスカスカで、どこが詰まっているか一目でわかるはずだ。
FSMは、普段は意識する必要がないほど優秀に働いている。でも、いざという時のパフォーマンスの「隠れた支配者」でもあるんだ。
「なぜ速いのか」「なぜ遅いのか」。その裏側にある構造を理解しているエンジニアは、トラブルに強い。また何か不明点があればいつでも聞いてくれ。一緒にPostgreSQLの深淵を覗こうじゃないか。
コメント