PostgreSQLの『work_mem』と上手に付き合う方法:パフォーマンスチューニングの勘所
こんにちは。データベースエンジニアとして現場を駆け回っていると、必ずと言っていいほどぶち当たる壁があります。それは「クエリの遅延」。
特に「さっきまでサクサク動いていたのに、なぜか特定のクエリだけ急に遅くなった……」なんて経験、一度や二度じゃないはずです。その原因の多くは、実はメモリの使い方が最適化されていないことにあります。
今日は、PostgreSQLのパフォーマンスチューニングにおいて、最も重要かつ、最も繊細な設定値の一つである`work_mem`について、現場の知見を交えてお話ししますね。
—
`work_mem`ってそもそも何者?
簡単に言えば、「1つのクエリがソートやハッシュ結合を行うときに、メモリ上で使える作業領域」です。
PostgreSQLは、`ORDER BY`や`DISTINCT`によるソート、あるいは`HASH JOIN`による結合を行う際、まずはメモリ内で処理を完結させようとします。もしメモリが足りなければ、OSのディスク(一時ファイル)に書き出すわけですが、これが遅い。もう、圧倒的に遅い。
つまり、`work_mem`を増やすことは、「ディスクへの退避を防ぎ、爆速を維持する」ための近道なんです。
ここが落とし穴!「増やせばいい」は間違い
「じゃあ、`work_mem`を1GBくらいにドカンと増やせば最強じゃん!」
……そう思った方、ちょっと待ってください。これが多くの初心者が陥る罠です。
実は`work_mem`、「クエリ実行中の同時接続数」×「クエリ内のソートノード数」で消費されます。もし`work_mem`を大きくしすぎて、同時に多数のクエリが走るとどうなるか。サーバーの物理メモリが枯渇して、OSがOOM Killerを発動させ、PostgreSQLプロセスが強制終了される……という悪夢が待っています。
実践的なチューニングのステップ
では、どうやって適正値を見極めるのか。僕が現場で行っている手順を教えますね。
1. 「ディスク書き出し」が起きているか確認する
まず、特定のクエリが遅い原因が本当にメモリ不足かを確認します。`EXPLAIN ANALYZE`を叩いてみてください。
EXPLAIN ANALYZE SELECT FROM large_table ORDER BY created_at DESC;
実行結果に以下のような表示があれば、`work_mem`が足りていません。
> `Sort Method: external merge Disk: 10240kB`
「Disk」という単語が見えたら赤信号です。
2. 適切な値を算出する
まずは、現在の`work_mem`の設定を確認します。
SHOW work_mem;
もし`4MB`程度なら、`16MB`や`32MB`くらいまで様子を見ながら上げてみましょう。ただし、計算式はこうです。
- `(サーバーの物理メモリ – OSが使う分 – shared_buffers) / (同時接続数 1クエリあたりのノード数)`
この計算式、最初は難しく感じるかもしれませんが、まずは「全体をメモリの何割使うか」という感覚を掴むのが大事です。
3. グローバル設定ではなく「セッション単位」で調整する
ここがプロの技です。`postgresql.conf`でグローバルに大きくしすぎるとリスクが高い。なので、特定の重いバッチ処理や、複雑な集計クエリを実行する直前だけ設定を変えるのが賢いやり方です。
— 重い処理の直前だけ一時的に増やす
SET work_mem = ’64MB’;
— 重いクエリを実行
SELECT … FROM … ORDER BY …;
— 元に戻す(忘れずに!)
RESET work_mem;
最後に:先輩からのアドバイス
`work_mem`の調整は、いわば「バランスゲーム」です。メモリを潤沢に使える環境なら少し強気になってもいいですが、クラウド環境などでメモリが限られている場合は、インデックスを貼ってソートそのものを回避したり、クエリを分割したりするアプローチの方が本質的な解決になることも多いです。
「数値を変えれば速くなる」と信じるのもいいですが、まずは`EXPLAIN`と友達になって、「なぜメモリが足りないのか」をログから読み解く癖をつけてください。それが、DBエンジニアとして確実にステップアップする近道です。
また現場で詰まったら、いつでも聞いてくださいね。一緒に最適解を探しましょう!
コメント