【実務・中級編】 VACUUM戦略 – PostgreSQL

「ねえ、PostgreSQLを運用していて『なんだか最近、テーブルの更新が重いな』とか『ストレージの空き容量が減る一方だな』って思ったことない?」

そう言われてドキッとしたあなた、もしかしてVACUUM(バキューム)のことを「ただのゴミ掃除」だと思って放置していませんか?

PostgreSQLの世界において、VACUUMは単なるクリーンアップじゃありません。データベースの心臓部を健やかに保ち、パフォーマンスを維持するための「生命線」です。今回は、現場で泥臭くPostgreSQLを触ってきた僕が、教科書には書いていない「生きたVACUUM戦略」について話そうと思います。

—

1. なぜVACUUMが必要なのか?(MVCCの宿命)

まず前提として、PostgreSQLのMVCC(多版同時実行制御)の仕組みを思い出してほしいんだ。

PostgreSQLはデータを更新(UPDATE)したり削除(DELETE)したりしても、古いレコードを物理的にすぐ消すわけじゃない。古い行を「デッドタプル」として残したまま、新しい行を書き込む。これによって、「読み取りと書き込みがブロックし合わない」という素晴らしい並行性が実現されているわけだけど、その代償として「死んだデータが溜まり続ける」という問題が発生する。

このゴミを片付けて、再利用可能な領域(FSM: Free Space Map)に戻すのがVACUUMの役割なんだ。

2. 自動VACUUM(Autovacuum)を信じすぎるな

「でも、`autovacuum`が動いているから大丈夫でしょ?」

そう思うよね。確かにデフォルトの設定は優秀だけど、それはあくまで「汎用的な環境」の話。データ量がテラバイト級になったり、更新頻度が激しいテーブルがあったりする場合、デフォルトの閾値だと「掃除が追いつかない」なんてことはザラにある。

もし、`pg_stat_user_tables`を見て `n_dead_tup`(デッドタプルの数)がとんでもない数字になっていたら要注意。そのテーブルは今、肥大化して検索効率が劇的に落ちているはずだ。

実践的なチューニングのヒント

テーブルごとにAutovacuumの感度を調整するのが、腕の見せ所だよ。

— 特定のテーブルだけ、ゴミが少し溜まったらすぐに掃除させる設定
ALTER TABLE orders SET (
autovacuum_vacuum_scale_factor = 0.01, — 1%の更新で発動
autovacuum_vacuum_cost_limit = 1000 — 負荷をかけてもいいから早く終わらせる
);

3. 手動VACUUMは「いつ」やるべきか?

基本はAutovacuumにお任せでいい。でも、現場で「手動でVACUUMを投げなきゃ!」となる瞬間がある。

  • 大量のDELETE/UPDATEをした直後: バッチ処理で数百万件を一気に削除した時なんかは、Autovacuumの起動を待たずに即実行するのが定石。「領域を即座に解放したい」時だね。
  • インデックスの肥大化を防ぎたい時: `VACUUM ANALYZE`をかけることで、統計情報も更新される。プランナが正しい実行計画を選べるように、大きな更新の後はセットで実行する癖をつけておこう。

ただし、注意点。`VACUUM FULL`は気をつけて。これはテーブルを丸ごとロックするから、本番環境でやるとサービスが止まる。「掃除の間、店を閉める」ようなものだ。基本はロックをかけない通常の`VACUUM`を使おう。

4. トランザクションID(XID)の周回という「時限爆弾」

最後に、これだけは絶対に忘れないでほしい。「VACUUMはサボるとデータベースが停止する」という事実。

PostgreSQLはトランザクションを識別するためにIDを振っているんだけど、このIDには上限がある。古いトランザクションをVACUUMで消さないと、IDが一周してしまい、最悪の場合「データが読み込めなくなる(周回防止のための停止)」という壊滅的な事態に陥る。

`age(relfrozenxid)`が大きくなってきたら危険信号。監視ツールでこの値を必ずモニタリングしておくこと。

— トランザクションの年齢を確認(値が大きすぎると危険!)
SELECT relname, age(relfrozenxid)
FROM pg_class
WHERE relkind = ‘r’
ORDER BY age(relfrozenxid) DESC LIMIT 10;

—

現場の先輩からのアドバイス

VACUUMは「日々の積み重ね」だよ。

1. まずは現状を知る: `pg_stat_user_tables`を定期的に眺める習慣をつける。
2. 適切な閾値を設定する: 全テーブル一律ではなく、アクセスの多いテーブルは個別にチューニングする。
3. 監視を怠らない: XIDの周回は命取り。アラートは必ず設定しておく。

データベースは、面倒を見てあげた分だけ必ず応えてくれる。難しく考えすぎず、まずは自分のデータベースの「健康状態」を覗くところから始めてみよう。

もし分からないことがあれば、またいつでも聞いてくれ。一緒にPostgreSQLを育てていこうぜ!

コメント

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