PostgreSQLの「共有バッファ(Shared Buffers)」を攻略して、パフォーマンスの壁を突破しよう
やあ。最近、PostgreSQLのパフォーマンスチューニングに頭を悩ませている後輩が増えてきた気がするね。
「SQLは最適化したはずなのに、なぜかレスポンスが改善しない…」
「大量の読み込みが発生すると、急にデータベースの負荷が跳ね上がる…」
そんな時に、一番最初に見るべきポイントが「共有バッファ(shared_buffers)」だ。今日は、教科書的な説明はさらっと流して、現場で役立つ「なぜそれが重要なのか」という本質的な話をしよう。
—
1. そもそも共有バッファは何をしているのか?
一言で言うと、PostgreSQLの「作業机」だと思えばいい。
ディスク(SSDやHDD)からデータを読み込むのは、どんなに速いストレージを使っても、メモリから読み込むのに比べれば「永遠」とも言えるほど遅い。だから、PostgreSQLは一度読んだデータをメモリ上にキャッシュしておくんだ。その場所が共有バッファだ。
もし必要なデータが共有バッファにあれば、ディスクを叩かずにメモリから即座に返せる。これを「バッファヒット」と呼ぶ。チューニングの極意は、いかにして「ディスクへのアクセスを減らし、バッファヒット率を上げるか」に尽きるわけだ。
2. なぜ共有バッファのサイズ設定が重要なのか
PostgreSQLの `postgresql.conf` を見ると、デフォルトではかなり控えめなサイズになっていることが多い。これは、どんな環境でもとりあえず動くようにという配慮なんだけど、本番環境でこれをそのままにするのは「フェラーリのエンジンを軽自動車に積んで走る」ようなものだ。
じゃあ、大きくすればするほどいいのか? そういうわけじゃない。
- 小さすぎると: 頻繁にディスクI/Oが発生し、CPUは「データの到着待ち」で暇になる(I/O Wait)。
- 大きすぎると: OSが使えるメモリが減り、OS側のキャッシュ(ページキャッシュ)が圧迫される。また、PostgreSQLの管理コストも増える。
一般的には「搭載メモリの25%程度」が目安と言われるけれど、これはあくまでスタートラインだ。
3. 実践:今のバッファヒット率を計測する
まずは現状把握だ。以下のSQLを叩いてみてほしい。
SELECT
sum(heap_blks_read) as disk_read,
sum(heap_blks_hit) as buffer_hit,
(sum(heap_blks_hit) – sum(heap_blks_read)) / sum(heap_blks_hit + heap_blks_read) 100 as hit_ratio
FROM pg_statio_user_tables;
この `hit_ratio` が99%以上あれば、君のDBはかなり健康だ。逆に90%を切っているなら、共有バッファのサイズを見直すか、インデックスの設計(物理設計)を疑ったほうがいい。
4. 現場の教訓:バッファは「魔法の杖」ではない
ここからが先輩からのアドバイスだ。バッファを増やせばすべて解決すると思ったら大間違いだよ。
- インデックスなしの全スキャンは無意味:
どんなに共有バッファが巨大でも、インデックスを使わない `SELECT ` を繰り返せば、キャッシュは一瞬で掃き出される(これをキャッシュ汚染と言う)。「バッファが足りないから増やす」前に、「SQLが効率的か」を確認するのが先決だ。
- OSのキャッシュも味方につける:
PostgreSQLは共有バッファだけでなく、OSレベルのファイルシステムキャッシュも活用する。共有バッファを巨大にしすぎると、OSのキャッシュ領域が削られてしまい、結果的にトータルパフォーマンスが落ちることもある。バランス感覚が大事だ。
最後に:チューニングは「観測」から
もし共有バッファの値を変更するなら、一度に大きく変えちゃダメだ。少しずつ増やして、`pg_stat_activity` や `pg_stat_statements` を見て、実際にクエリの実行時間がどう変化したかをグラフ化する。
エンジニアの仕事は、勘で設定を変えることじゃない。「なぜその設定にしたのか」をデータで説明できるようになることだ。
もし、設定を変えても「バッファヒット率は高いのに遅い」という現象に突き当たったら、次は「ロック待ち」や「チェックポイントの頻度」を疑う番だ。その話はまた今度しよう。
現場からは以上だ。困ったことがあったら、いつでもコードを見せてくれよ。
コメント