データベースの「心臓部」を覗く:PostgreSQLのページレイアウトを理解する
やあ。今日はちょっとディープな話をしようか。
普段、我々は`SELECT`や`UPDATE`といったSQLを通じてデータベースと会話しているけれど、その裏側でPostgreSQLがどうやってデータを「物理的な箱(ページ)」に詰め込んでいるのか、意識したことはあるかな?
「PostgreSQLはMVCCだから…」なんて言葉はよく聞くけれど、具体的にデータがディスク上でどう並んでいるかを知ると、クエリのチューニングやバキュームの挙動に対する解像度がグッと上がるんだ。今日は、PostgreSQLの最小単位である「8KBのページ」の構造を、現場の視点から解剖してみよう。
—
1. 8KBの世界:ページの基本構造
PostgreSQLのデータファイルは、8KBの「ページ(またはブロック)」という単位で分割されている。このページの中身は、大きく分けて4つのセクションで構成されているんだ。
- ページヘッダ (Page Header): ページの管理情報。ここにはLSN(ログシーケンス番号)や、空き領域がどこから始まるかといったポインタが書き込まれている。
- アイテムポインタ (Item Pointers): ページ内のタプル(行データ)がどこにあるかを示す、小さな配列(Line Pointer)。
- 空き領域 (Free Space): まだ何も入っていない、新入りタプルのためのスペース。
- タプル (Tuples): 実際のデータ本体。
面白いのは、データ(タプル)はページの後ろ側から、ポインタは前側から中央に向かって詰められていくという点だ。この「中央で出会う」ような配置のおかげで、空き領域を効率的に管理できる仕組みになっているんだね。
2. なぜ「アイテムポインタ」が重要なの?
ここが一番のポイントだ。PostgreSQLのページを覗くと、ヘッダの直後に「アイテムポインタ」が並んでいる。なぜ直接タプルを指さないのか?
それは、ページ内でタプルの位置が変わる可能性があるからだ。
例えば、ページ内でデータを再配置(デフラグ)したり、タプルを更新してサイズが変わったりしたとき、物理的な位置はズレる。でも、インデックス側が持っている「タプル識別子(TID)」は「◯番目のポインタを見てね」という情報なので、ポインタさえ更新しておけば、インデックスを丸ごと書き換える必要がなくなる。
この「間接参照」の仕組みこそが、PostgreSQLの柔軟なデータ操作を支える知恵なんだ。
3. ちょっと実験してみよう
言葉だけじゃピンとこないよね。`pageinspect`という拡張モジュールを使うと、実際にページの中身を覗けるんだ。
— 拡張のインストール(一度だけ)
CREATE EXTENSION pageinspect;
— 特定のテーブルの0ページ目を見てみる
SELECT FROM heap_page_items(get_raw_page(‘your_table_name’, 0));
これを実行すると、`lp`(アイテムポインタ番号)、`lp_off`(オフセット)、`t_ctid`(次のバージョンのタプルへの参照)などがズラッと出てくるはずだ。
もし、`VACUUM`をかけていないテーブルで大量に`UPDATE`を繰り返した後にこれを見ると、古いバージョンのタプル(Dead Tuple)がページ内に残っているのが見て取れる。これが「なぜバキュームが必要なのか」という問いに対する、物理的な答えだよ。
4. 現場で意識すべきこと:ページレイアウトが教えてくれること
この構造を知っていると、こんな判断ができるようになる。
1. FILLFACTORの重要性:
`CREATE TABLE … WITH (fillfactor = 80)` なんて設定を見たことはないかな?これはページを100%詰め込まず、20%の余白を残す設定だ。頻繁に`UPDATE`が発生するテーブルでこれを設定しておけば、ページ内に空き領域があるから、わざわざ新しいページを確保しなくてもその場でタプルを更新できる。つまり、パフォーマンスが落ちにくいんだ。
2. テーブル肥大化の予兆:
`pg_stat_user_tables`で`n_dead_tup`が増えているのを見たら、「あ、今ページの中で空き領域が散らばっていて、有効なデータがポインタに阻まれて配置効率が悪くなっているな」と想像できる。
—
最後に
データベースの物理構造なんて、普段は忘れていても仕事はできる。でも、パフォーマンスが詰まったときに「ページの中身がどうなっているか」を想像できるエンジニアは、強さが違う。
「なぜこのクエリは遅いのか?」と悩んだとき、まずは `pageinspect` で中を覗いてみてほしい。そこには、機械的なログからは見えない、PostgreSQLの誠実な(あるいは必死な)仕事ぶりが刻まれているはずだから。
また何か気になったら聞いてくれ。現場からは以上だ!
コメント