【テクニカル・上級編】 work_mem設定 – PostgreSQL

PostgreSQLの「work_mem」を巡る孤独な戦い:メモリとディスクの境界線

PostgreSQLのチューニングにおいて、`work_mem`ほどエンジニアのセンスが問われるパラメータは他にないかもしれません。

「とりあえず大きくしておけば速くなるだろう」という考えで安易に値を上げ、数日後にOOM Killerにシステムを蹂躙された経験がある方も多いはずです。逆に、慎重になりすぎてデフォルト値のまま放置し、本来なら一瞬で終わるはずのソート処理が、ディスクI/Oの悲鳴と共に何分もかかっている現場も後を絶ちません。

今日は、この「諸刃の剣」である`work_mem`について、少し深掘りしてみましょう。

なぜ「一時ファイル」は我々の敵なのか

`work_mem`は、ソート(ORDER BY、DISTINCT)やハッシュ結合(Hash Join)といった操作が、メモリ上で完結できるか、それとも物理ディスクへ溢れるかの境界線です。

PostgreSQLは、このメモリが足りないと判断した瞬間、一時ファイル(Temporary Files)を作成し、スワップアウトを開始します。SSD全盛の時代とはいえ、メモリのレイテンシとディスクI/Oのレイテンシには、依然として数桁の壁が存在します。

特に厄介なのは、この処理がクエリの実行計画(プラン)に依存している点です。「このクエリはメモリに乗る」とプランナが判断したとしても、実際のデータ分布や統計情報のズレによって、実行時にメモリを食いつぶすことは珍しくありません。

統計情報と実行計画の相関

`work_mem`の最適化において、まず疑うべきは設定値そのものではなく、統計情報の鮮度です。

`ANALYZE`を怠り、テーブルの行数が実態と乖離していると、プランナはハッシュテーブルのサイズを見誤ります。本来ならもっと大きなメモリ領域を確保すべきところを、小さな見積もりで始めてしまい、結果としてメモリ不足を引き起こす。これはチューニングというよりは、土台作り(フィジカル設計)の問題です。

まずは `log_temp_files` を設定してみてください。これを行うだけで、どのクエリが、どの程度のサイズの一時ファイルを作成しているかが、ログに浮き彫りになります。「何が起きているか」を観測すること。これが全ての出発点です。

「最大同時実行数」という罠

よく陥るミスが、`work_mem`を設定する際に「サーバーの物理メモリ」だけを見て決めてしまうことです。

重要なのは、「その瞬間に同時に動く可能性のあるクエリの数」です。

(最大同時実行クエリ数) × work_mem ≦ (利用可能なメモリ)

この不等式を無視して`work_mem`を1GBに設定し、10個のクエリが同時に重いJOINを叩けば、それだけで10GBのメモリが消費されます。OSのキャッシュや、他のプロセスが使うメモリとの兼ね合いを考えれば、計算はもっとシビアにならざるを得ません。

最近の私の現場でのアプローチはこうです。
1. `work_mem`をグローバルに大きくするのではなく、セッション単位、あるいは特定のバッチ処理単位で調整する。
2. `SET work_mem = ’64MB’;` のように、必要なクエリの直前で動的に変更する。

こうすることで、システム全体への影響を最小限に抑えつつ、クリティカルな処理のパフォーマンスを最大限に引き出すことができます。

最後に:完璧な設定値など存在しない

PostgreSQLは非常に正直なデータベースです。設定を変えれば、その分だけ挙動に変化が現れます。

「`work_mem`をいくらにすればいいですか?」という質問に対する、世界最高峰の答えは間違いなくこうです。「あなたのワークロードを、ログとプロファイラで観察しなさい」。

一時ファイルが生成されているクエリを特定し、そのクエリが本当にインデックスだけで解決できないものなのかを確認する。もしフルスキャンが避けられないのであれば、そこだけピンポイントでメモリを割り当てる。この泥臭いプロセスの積み重ねこそが、エンジニアの腕の見せ所です。

銀の弾丸を探すのはやめましょう。代わりに、PostgreSQLというエンジンの鼓動を、統計情報とログから読み解く力を磨いてください。その先には、ディスクI/Oの音もしない、静かで高速なデータベースの世界が待っています。

コメント

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