【実務・中級編】 バッファプールマネージャ – PostgreSQL

PostgreSQLの「心臓部」を理解する:バッファプールマネージャとクロック掃引の秘密

やあ。今日もPostgreSQLと格闘してるかい?

普段、何気なくクエリを投げて「速い・遅い」で一喜一憂しているけれど、その裏でPostgreSQLがどんな「やりくり」をしているか、ふと考えたことはあるかな。

今日は、PostgreSQLのパフォーマンスを語る上で避けては通れない、バッファプールマネージャの話をしよう。これを知っていると、PostgreSQLの挙動が単なる「ブラックボックス」から「制御可能なツール」に見えてくるはずだ。

—

そもそも、なんでバッファプールが必要なの?

結論から言うと、「ディスクI/Oは死ぬほど遅いから」だ。

メモリ(RAM)へのアクセスがナノ秒単位なのに対し、ディスク(特にHDDや、たとえ高速なSSDであっても)へのアクセスはミリ秒単位。数桁も違う。だからPostgreSQLは、一度ディスクから読み込んだページを、共有メモリ内の「バッファプール」という場所に大事にキャッシュしておくんだ。

ここまでは基本だよね。でも、メモリは無限じゃない。新しいデータを読み込みたいのにメモリがいっぱいになったら、誰かを追い出さないといけない。そこで登場するのがページ置換アルゴリズムだ。

クロック掃引(Clock Sweep)アルゴリズムという賢い選択

PostgreSQLでは、LRU(Least Recently Used:最も長い間使われていないものを捨てる)を改良した、クロック掃引(Clock Sweep)アルゴリズムが採用されている。

LRUをそのまま実装しようとすると、アクセスするたびにリストの順序を入れ替える必要があって、マルチスレッド環境ではロックの競合が頻発してしまうんだ。PostgreSQLの設計者は、そこを避けつつ効率的に動く仕組みとしてこれを選んだわけだ。

どう動いているのか?

イメージしてほしい。メモリ上の各ページには「参照ビット(usage_count)」がついている。

1. 時計の針(NextVictimBuffer)が、バッファプールをぐるぐる回る。
2. ページにアクセスがあるたびに、そのページの`usage_count`をインクリメントする(上限あり)。
3. メモリが足りなくなると、時計の針が指したページを確認する。

  • `usage_count > 0` なら:その値を1減らして、「今回は見逃してやるよ」と言って針を進める。
  • `usage_count == 0` なら:「お前はもう用済みだ」と判断し、そのページを追い出して新しいデータをそこへロードする。

この「一度の判定で消すのではなく、チャンスをあげる」という仕組みが、シンプルかつ高効率なんだ。

—

実務で意識すべき「バッファ」のサイン

この仕組みを知っておくと、パフォーマンスチューニングの視点が変わる。例えば、`pg_stat_activity`や`pg_buffercache`といった拡張モジュールを使って、バッファの状態を覗いてみよう。

1. バッファキャッシュの偏り

特定のテーブルやインデックスがバッファプールを占有しすぎると、他の重要なクエリがディスクI/Oを強いられる(バッファの汚染)。
もし「特定のクエリがやけに遅いな?」と思ったら、`pg_buffercache`を使って、どのテーブルのページがメモリを占領しているか確認してみるといい。

— どのテーブルがバッファを多く占有しているかチェックするクエリ
SELECT c.relname, count() AS buffers
FROM pg_buffercache b
INNER JOIN pg_class c ON b.relfilenode = pg_relation_filenode(c.oid)
AND b.reldatabase IN (0, (SELECT oid FROM pg_database WHERE datname = current_database()))
GROUP BY c.relname
ORDER BY 2 DESC
LIMIT 10;

2. shared_buffersの適正値

よくある初心者のミスが「`shared_buffers`を大きくすればするほど速くなる」という思い込みだ。
OSのファイルシステムキャッシュとPostgreSQLのバッファキャッシュは二重管理になりがち。メモリが足りなくなってOSのページキャッシュまで追い出されると、逆に性能がガタ落ちする。

一般的にはOSメモリの25%程度が定石と言われるけれど、ワークロードに合わせて「実際にキャッシュが効いているか(Cache Hit Ratio)」をモニタリングしながら調整するのがプロの仕事だ。

—

最後に:エンジニアへのアドバイス

バッファプールマネージャは、PostgreSQLが「どうやって限られたリソースで最善を尽くすか」を考えている場所だ。

「なぜこのクエリは初回は遅いのに、2回目は爆速なのか?」
「なぜこの大きなバッチ処理のあとに、他のクエリが一時的に遅くなるのか?」

そんな疑問を持ったとき、ぜひこの「クロック掃引」のイメージを思い出してほしい。データがメモリ上をどう移動し、ディスクとどう折り合いをつけているか。その視点を持つだけで、君が書くクエリやデータベースの設計は、間違いなく一歩上のレベルに到達するはずだよ。

何か詰まったら、いつでも聞いてくれ。一緒にPostgreSQLの深淵を覗こうじゃないか。

コメント

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