【実務・中級編】 maintenance_work_mem – PostgreSQL

PostgreSQLの隠れた「影の主役」:maintenance_work_mem を使いこなせ!

やあ。データベースの運用、お疲れ様。
最近、PostgreSQLのチューニングについて相談を受けることが増えたんだけど、みんな意外と「SQLの書き方」や「work_mem」には気を配るのに、`maintenance_work_mem` のことを忘れがちだ。

正直に言うよ。この設定値を放置していると、ある日突然、メンテナンス処理が驚くほど遅くなって、深夜の運用アラートに悩まされることになる。今回は、この「縁の下の力持ち」について、現場の知見を詰め込んで解説するよ。

—

maintenance_work_mem って結局何者?

簡単に言うと、PostgreSQLが「メンテナンス作業」をするときに使う専用の作業用メモリだ。
対象になるのは主に以下の操作だ。

  • VACUUM / VACUUM ANALYZE(領域の掃除と統計情報の更新)
  • CREATE INDEX(インデックス作成)
  • ALTER TABLE ADD FOREIGN KEY(外部キー制約の追加)
  • REINDEX(インデックスの再構築)

普段のSQLクエリが `work_mem` を使うのに対して、こっちは「データベースを健康に保つための重たい処理」で使われる。デフォルト値は 64MB なんて控えめな数字だけど、大規模なテーブルを運用しているなら、このサイズがボトルネックになるのは目に見えているよね。

—

なぜこの設定が重要なのか?

例えば、数百万件あるテーブルで `VACUUM` を走らせるとしよう。`maintenance_work_mem` が小さいと、PostgreSQLは死んだレコード(デッドタプル)のリストをメモリに収めきれず、何度もディスクに書き出しながら作業することになる。

結果はどうなるか?

  • I/O負荷の急増: ディスクへの書き込みが増え、本来のクエリ処理を圧迫する。
  • 処理時間の増大: 本来なら10分で終わるはずのメンテナンスが、1時間以上かかる。

特に `CREATE INDEX` を行う時、このメモリが小さいとインデックスの構築速度が劇的に落ちる。実務では、「インデックス作成中にサイトが重くなった」なんてトラブルの主犯格がこれだったりするんだ。

—

現場での設定目安:どう決める?

じゃあ「とりあえず最大値にすればいいのか?」というと、それは危険だ。このメモリはセッションごとに確保される。もし `maintenance_work_mem` を極端に大きくして、並列でメンテナンス処理が走ったら、サーバーのメモリが一気に枯渇して OOM Killer に叩かれる可能性がある。

僕が現場でよく使うガイドラインはこんな感じだ。

1. 物理メモリの 5% 〜 10% 程度を上限の目安にする。
2. サーバーが32GBメモリなら、1GB〜2GBくらいをベースに調整する。
3. ただし、`autovacuum` が並列で動くことを考慮して、`autovacuum_max_workers` とのバランスを見る。

もし、特定のテーブルでインデックスを張り直す時だけ速くしたいなら、そのセッション内だけで一時的に増やすのが一番賢いやり方だ。

— 一時的にこのセッションだけメモリを増やす
SET maintenance_work_mem = ‘2GB’;

— その後で重い処理を実行する
CREATE INDEX CONCURRENTLY idx_users_created_at ON users(created_at);

こうすれば、サーバー全体のメモリを圧迫せずに、必要な時だけブーストをかけられる。これができるのが、わかっているエンジニアのやり方さ。

—

注意点:インデックス作成時の「並列化」

最近のPostgreSQL(バージョン11以降)では、`CREATE INDEX` が並列で実行できるようになった。このとき、指定した `maintenance_work_mem` は「並列実行される各ワーカー」に割り当てられる。

例えば `maintenance_work_mem = 1GB` で `max_parallel_maintenance_workers = 4` に設定していると、理論上は最大 4GB 近くのメモリを消費する可能性がある。この計算だけは忘れないようにしてほしい。

—

最後に:まずは現状を知ろう

君の担当しているDBで、最近 `VACUUM` がやけに遅いと感じることはないか?
まずは `pg_stat_activity` を覗いて、メンテナンス処理がどんな状況か確認してみよう。

SELECT pid, query, state, backend_start
FROM pg_stat_activity
WHERE query LIKE ‘%VACUUM%’ OR query LIKE ‘%CREATE INDEX%’;

もし「もっと速くしたい」と思ったら、まずは `maintenance_work_mem` を今の2倍、3倍に増やしてみることから始めてみてくれ。劇的に世界が変わるはずだ。

データベースチューニングに魔法はない。あるのは「仕組みを理解して、地道に調整する」ことだけだ。困ったことがあったら、またいつでも聞いてくれよ。応援しているぞ。

コメント

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