こんな状況に直面したことはないでしょうか。PostgreSQLのmax_connectionsを単純に増やすと、コネクション1本あたり数MB~数十MBのメモリを消費するため、サーバーが詰まる問題は根本的に解決しません。
そこで登場するのがpgBouncerです。アプリとPostgreSQLの間に入って接続をプールし、PostgreSQL本体への接続数を大幅に削減します。アプリ側は数百コネクションを持てるのに、PostgreSQL実体への接続は数十本に抑えるという、LinuxサーバーのDB運用では定番の構成です。
この記事では、pgBouncerのインストールからpgbouncer.iniの設定・動作確認・監視まで、Rocky Linux 9.4 / Ubuntu 24.04 LTSで動作確認した手順を解説します。
この記事のポイント
・pgBouncerをアプリとPostgreSQLの間に挟むことで、PostgreSQL本体の同時接続数を削減できる
・pgbouncer.iniの[databases]と[pgbouncer]セクションを設定し、userlist.txtでパスワードを登録する
・pool_modeはtransactionが最も効率的だが、prepared statementの扱いに注意が必要
・SHOW STATSとSHOW POOLSでリアルタイムに接続状況を監視できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
pgBouncerとは何か — PostgreSQLの接続数問題を解消するコネクションプーラー
PostgreSQLは接続ごとにプロセスを生成する(プロセスモデル)アーキテクチャです。この設計は安定性と分離性に優れる一方、同時接続数が多いと以下の問題が発生します。・メモリ消費:コネクション1本あたり最低5~10MBのメモリを消費する。100接続で500MB以上が接続維持だけで消える
・プロセス起動コスト:短命なコネクションを大量に張ると、forkcのオーバーヘッドが無視できなくなる
・max_connectionsの上限:デフォルト100。増やせるが、リソース消費との兼ね合いで無制限には増やせない
pgBouncerはアプリとPostgreSQLの間に立つプロキシで、アプリからの接続をプールして再利用します。アプリ側は「1リクエスト1コネクション」のように見えても、pgBouncer側でPostgreSQLへのコネクションを使い回すため、PostgreSQL本体への接続数を劇的に減らせます。
| 比較項目 | pgBouncerなし | pgBouncerあり |
|---|---|---|
| アプリ→DB接続数 | アプリスレッド数分(例: 200) | pool_size設定値(例: 20) |
| PostgreSQLメモリ消費 | 大(接続数×5~10MB) | 小(プール数×5~10MB) |
| コネクション確立コスト | 毎リクエスト発生 | 初回プール確立のみ |
| 設定の複雑さ | 不要 | pgbouncer.ini + userlist.txtが必要 |
pgBouncerをLinuxサーバーへインストールする
1. Rocky Linux / RHEL 9 でのインストール
Rocky Linux 9ではAppStreamリポジトリにpgBouncerが含まれています。# パッケージを確認 dnf list pgbouncer # インストール sudo dnf install -y pgbouncer # バージョン確認 pgbouncer --version
PgBouncer 1.22.0
2. Ubuntu 24.04 LTS でのインストール
sudo apt update sudo apt install -y pgbouncer pgbouncer --version
3. 設定ファイルの場所を確認する
# Rocky Linux / RHEL ls /etc/pgbouncer/ # Ubuntu ls /etc/pgbouncer/
pgbouncer.iniの基本設定 — [databases]と[pgbouncer]セクションの書き方
pgBouncer の設定ファイルは大きく2セクションに分かれます。1. [databases]セクション — 接続先DBを定義する
[databases] # エイリアス名 = host=接続先 port=ポート dbname=DB名 myapp = host=127.0.0.1 port=5432 dbname=myapp_db # 全DB名でそのまま受け付ける場合(ワイルドカード) # * = host=127.0.0.1 port=5432
2. [pgbouncer]セクション — リスニングと接続数を設定する
[pgbouncer] # pgBouncerがリッスンするアドレスとポート listen_addr = 127.0.0.1 listen_port = 6432 # 認証方式(scram-sha-256 または md5) auth_type = scram-sha-256 auth_file = /etc/pgbouncer/userlist.txt # プールモード(後述) pool_mode = transaction # DB単位の最大コネクション数(PostgreSQL側の接続数上限) max_client_conn = 200 default_pool_size = 20 # ログ設定 logfile = /var/log/pgbouncer/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid # 管理DB(SHOW STATS等で使用) admin_users = pgbouncer_admin stats_users = pgbouncer_stats
・max_client_conn:pgBouncerがアプリ側から受け付ける最大接続数。PostgreSQLのmax_connectionsより大きくてよい
・default_pool_size:pgBouncerがPostgreSQL側に維持するコネクション数。PostgreSQLの実接続数がこの数値に制限される
・listen_port:6432がpgBouncer、5432がPostgreSQL本体。アプリは6432に向ける
userlist.txtでパスワード認証を設定する
pgBouncerはPostgreSQLへの接続時に自分が代わりに認証を行います。userlist.txtに接続に使うユーザー名とパスワードのハッシュを登録します。1. PostgreSQLでパスワードハッシュを取得する
# PostgreSQLに接続 sudo -u postgres psql # pgBouncerが使う接続ユーザーのハッシュを確認 SELECT usename, passwd FROM pg_shadow WHERE usename = 'myapp_user';
usename | passwd ------------+------------------------------------------------------------------------ myapp_user | SCRAM-SHA-256$4096:xxxxxxxxxxxxxxxx==$yyy...==:zzz...== (1 row)
2. userlist.txtに登録する
# /etc/pgbouncer/userlist.txt の形式 # "ユーザー名" "パスワードハッシュ" "myapp_user" "SCRAM-SHA-256$4096:xxxxxxxxxxxxxxxx==$yyy...==:zzz...==" "pgbouncer_admin" "md5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
プールモード(session/transaction/statement)の選び方
pool_modeはpgBouncerの動作を決める最重要パラメータです。| pool_mode | コネクション解放タイミング | 特徴・注意点 |
|---|---|---|
| session | クライアントが切断したとき | 最安全。prepared statementも問題なし。プール効率は低い |
| transaction | トランザクション終了時(COMMIT/ROLLBACK) | 最効率。prepared statementはSET session変数を使うと問題あり(後述) |
| statement | SQLステートメント1文の終了時 | トランザクションが使えない。特殊用途のみ |
transactionモードが最も効率的です。ただし、以下のパターンはtransactionモードで問題が出ます。・プリペアドステートメント(PREPARE/EXECUTE):トランザクション間でサーバー側のprepared statementが保持されないためエラーになる。アプリ側でsimple query modeを使うか、sessionモードに切り替える
・SET文(SET search_path など):セッション変数がトランザクションをまたいで保持されないため、意図しない動作になる場合がある
Rails/Django/Laravel等のORMはデフォルトでprepared statementを使う設定のものが多いです。ORM側の設定で無効化するか、pgBouncer側でserver_reset_query = DISCARDを設定することで回避できます。
pgBouncerを起動して接続確認する
1. 設定ファイルの構文チェック
pgbouncer -d /etc/pgbouncer/pgbouncer.ini # エラーなしであれば設定ファイルは正常
2. systemctlで起動する
# 起動 sudo systemctl start pgbouncer # 自動起動を有効化 sudo systemctl enable pgbouncer # ステータス確認 sudo systemctl status pgbouncer
* pgbouncer.service - A lightweight connection pooler for PostgreSQL Loaded: loaded (/usr/lib/systemd/system/pgbouncer.service; enabled; preset: disabled) Active: active (running) since Thu 2026-09-25 10:12:34 JST; 3s ago Main PID: 12345 (pgbouncer) Tasks: 2 (limit: 23156) Memory: 2.3 M CPU: 8ms CGroup: /system.slice/pgbouncer.service +--12345 pgbouncer /etc/pgbouncer/pgbouncer.ini
3. psqlでpgBouncer経由の接続を確認する
# pgBouncer経由でPostgreSQLへ接続(ポート6432を指定) psql -h 127.0.0.1 -p 6432 -U myapp_user -d myapp # 接続できたら実際にPostgreSQL本体に繋がっているか確認 myapp=> SELECT version();
version --------------------------------------------------------------------------------------------------------- PostgreSQL 16.3 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.4.1 20231218 (Red Hat 11.4.1-3), 64-bit (1 row)
SHOW STATSとSHOW POOLSで接続状況を監視する
pgBouncerは内部管理用の仮想データベース(pgbouncer)を持っており、接続状況をリアルタイムで確認できます。1. 管理DBへ接続する
# admin_usersに設定したユーザーでpgbouncerDBへ接続 psql -h 127.0.0.1 -p 6432 -U pgbouncer_admin pgbouncer
2. SHOW POOLSで現在のプール状態を確認する
pgbouncer=# SHOW POOLS;
database | user | cl_active | cl_waiting | sv_active | sv_idle | sv_used | sv_tested | sv_login | maxwait ------------+-----------+-----------+------------+-----------+---------+---------+-----------+----------+--------- myapp | myapp_user| 8 | 0 | 8 | 12 | 0 | 0 | 0 | 0 (1 row)
・cl_active:pgBouncerに接続中のアクティブなクライアント数
・cl_waiting:プールが満杯でコネクション待ちのクライアント数(ここが増えると危険)
・sv_active:PostgreSQL側で使用中のコネクション数
・sv_idle:プール内の待機中コネクション数
・maxwait:最大待機秒数(増加したら`default_pool_size`を引き上げる目安)
3. SHOW STATSで累積統計を確認する
pgbouncer=# SHOW STATS;
database | total_xact_count | total_query_count | total_received | total_sent | total_xact_time | total_query_time | avg_xact_time | avg_query_time ----------+------------------+-------------------+----------------+------------+-----------------+------------------+---------------+---------------- myapp | 2481 | 8935 | 29843291 | 87654321 | 3271845201 | 1248901234 | 1318 | 139 (1 row)
よくある問題と対処法
エラー: prepared statement "..." does not exist
pool_mode = transactionのとき、アプリ側がprepared statementを使っていると発生します。対処法1 — pgBouncerにDISCARD ALLを設定する:
# pgbouncer.iniの[pgbouncer]セクションに追加 server_reset_query = DISCARD ALL
# database.yml(Rails) production: prepared_statements: false
エラー: authentication failed for user
userlist.txtのパスワードハッシュ形式とauth_typeが一致していない場合に発生します。・auth_type = scram-sha-256のとき: userlist.txtには`SCRAM-SHA-256$...`形式のハッシュを記載する
・auth_type = md5のとき: userlist.txtには`md5xxxxxxxx...`形式のmd5ハッシュを記載する
pg_shadowから取得したpasswd列の値をそのまま使うのが最も確実です。
設定変更を反映する(再起動不要)
pgbouncerはHUPシグナルで設定ファイルを再読み込みします。# systemd経由でリロード sudo systemctl reload pgbouncer # または管理DBから psql -h 127.0.0.1 -p 6432 -U pgbouncer_admin pgbouncer -c "RELOAD;"
ログでエラーを確認する
# リアルタイムでログを追う sudo journalctl -u pgbouncer -f # またはログファイル直接 sudo tail -f /var/log/pgbouncer/pgbouncer.log
本記事のまとめ
| やりたいこと | 設定・コマンド |
|---|---|
| pgBouncerをインストールする(RHEL系) | sudo dnf install pgbouncer |
| 接続先DBを定義する | pgbouncer.ini の [databases] セクション |
| アプリ側の最大接続数を設定する | max_client_conn = 200 |
| PostgreSQL側への実接続数を制限する | default_pool_size = 20 |
| 最も効率的なプールモードを使う | pool_mode = transaction |
| プールの状況をリアルタイム確認する | SHOW POOLS;(管理DB経由) |
| 設定を再起動なしで反映する | sudo systemctl reload pgbouncer |
PostgreSQLのチューニングや自前でのサーバー運用を体系的に学びたい方は、下記のセミナー情報もご覧ください。
Linux Master Pro Seminar の詳細を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:PostgreSQLのWALアーカイブとPITRで任意時点に復元する手順|archive_modeの設定からリカバリターゲット指定まで
- この記事の属するカテゴリ:データーベース管理へ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます