2026年7月3日

2026年7月3日

WordPressでPgBouncerによるDB接続プーリングを使う前に知ること:対応範囲と設定手順

最初に結論を書くと、PgBouncer は PostgreSQL 専用の接続プーラーで、WordPress 本体が使う MySQL/MariaDB の接続には使えません。WordPress コアは公式には MySQL と MariaDB だけを対象にしており、PostgreSQL では動作しません。この記事では、WordPress の周辺で PgBouncer が役立つ場面を整理し、プラグインなどから別の PostgreSQL に接続する場合の PgBouncer の設定手順と、WordPress 本体の接続数を減らしたい場合の現実的な代替策を説明します。

WordPress と PostgreSQL の関係を整理する

WordPress のデータベースクラス wpdb は mysqli を使って MySQL/MariaDB に接続し、コアやプラグインのクエリも MySQL の文法で書かれています。PostgreSQL に直接つなぐ仕組みはコアにありません(2026年10月現在)。

PostgreSQL で WordPress を動かそうとする試みとしては、MySQL 向けのクエリを書き換えるドロップイン(PG4WP などのプロジェクトとそのフォーク)があります。ただし、本体の更新に追従できていない時期があることや、プラグインが使う MySQL 固有の SQL までは変換しきれないことから、本番サイトでの利用はおすすめできません。試す場合も、検証用の環境にとどめ、問題が出たときに MySQL に戻せる状態を保ってください。

このため、WordPress で PgBouncer を使う場面は、主に次の2つです。

  1. WordPress とは別に PostgreSQL のデータベースがある: 業務システムや分析基盤の DB が PostgreSQL で、WordPress のプラグインや API 連携からそこへ接続するケースです。
  2. ヘッドレス構成で、フロントエンド側のアプリが PostgreSQL を使う: WordPress は MySQL のまま、別のアプリが PostgreSQL を使うケースです。

一方、「WordPress の DB 接続数が多すぎる」という悩みが出発点なら、PgBouncer ではなく MySQL 向けの手段を選びます。これは記事の後半で説明します。

PgBouncer が解決すること

PostgreSQL は、接続ごとにサーバー側でプロセスを1つ起動します。PHP のように「リクエストごとに接続して切断する」使い方をすると、接続の確立と後始末のたびにプロセスの起動と終了が起き、同時接続が増えるとメモリも圧迫します。

PgBouncer はアプリと PostgreSQL の間に入り、少数のサーバー接続を保ったまま、多数のクライアント接続にそれを貸し出します。PHP から見ると接続先が PgBouncer に変わるだけで、接続の確立は軽くなり、PostgreSQL の max_connections を大きくしなくても多くのリクエストを受けられます。

PgBouncer を設定する

ここでは、Web サーバーと同じホストか近いホストに PgBouncer を置き、ローカルの 6432 番ポートで待ち受ける構成を例にします。パッケージ名や設定ファイルの場所はディストリビューションによって異なるため、読み替えてください。作業は検証環境で試してから本番に適用します。

インストール

Debian/Ubuntu 系では、公式リポジトリまたは PostgreSQL のリポジトリからインストールできます。

sudo apt install pgbouncer
pgbouncer --version

プールモードや準備済み文の扱いは PgBouncer のバージョンで差があるため、バージョンを控えておきます。

pgbouncer.ini の基本設定

[databases]
; クライアントが指定する DB 名 = 実際の接続先
appdb = host=10.0.0.20 port=5432 dbname=appdb

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt

pool_mode = transaction
max_client_conn = 200
default_pool_size = 20

admin_users = pgbouncer_admin

max_client_conn は PgBouncer が受け付けるクライアント接続の上限、default_pool_size は DB とユーザーの組ごとに PostgreSQL へ張るサーバー接続の数です。サーバー接続の合計が PostgreSQL の max_connections を超えないように、他のアプリや管理用の接続の分を残して決めます。

認証ファイル

auth_file には、接続を許可するユーザーとパスワードのハッシュを書きます。平文のパスワードは書かず、PostgreSQL 側に保存されている SCRAM のハッシュを使います。

"app_user" "SCRAM-SHA-256$4096:dummy-salt$dummy-stored-key:dummy-server-key"
"pgbouncer_admin" "SCRAM-SHA-256$4096:dummy-salt$dummy-stored-key:dummy-server-key"

上の値はダミーです。実際の値は、PostgreSQL のスーパーユーザーで SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'app_user'; を実行して取得します。このファイルは PgBouncer のプロセスだけが読める権限にしておきます。ユーザーが多い場合は、ファイルの代わりに auth_query で PostgreSQL に問い合わせる方式もあります。

プールモードの選び方

PgBouncer には3つのプールモードがあります。

  • session: クライアントが切断するまでサーバー接続を占有します。互換性は高いものの、PHP の「リクエストの間だけ接続する」使い方では効果が限られます。
  • transaction: トランザクションの間だけサーバー接続を貸し出します。PHP から使う場合はこれが一般的です。
  • statement: 文ごとに貸し出します。複数の文にまたがるトランザクションが使えないため、用途は限られます。

transaction モードでは、セッションに状態を持たせる機能が期待どおりに動きません。SET で変更したセッション変数、セッション単位のアドバイザリーロック、LISTEN、一時テーブルなどは、次のトランザクションで別のサーバー接続に移る可能性があります。こうした機能を使うコードは、トランザクションの中で完結させるか、session モードの別プールに分けます。

準備済み文(prepared statement)も注意点です。新しい PgBouncer(1.21 以降)では max_prepared_statements を設定すると transaction モードでもプロトコルレベルの準備済み文を扱えますが、古いバージョンでは接続が切り替わるとエラーになります。PHP 側でエミュレーションを使えば、バージョンに関係なく回避できます。

WordPress のプラグインから PgBouncer 経由で接続する

WordPress から別の PostgreSQL に接続するコードは、テーマではなくプラグインか mu-plugin に置きます。接続情報は wp-config.php に定数として置き、値は環境変数から読み込むようにすると、Git などに秘密情報を残さずに済みます。

// wp-config.php(値は環境変数から読む)
define( 'APP_PG_HOST', getenv( 'APP_PG_HOST' ) ?: '127.0.0.1' );
define( 'APP_PG_PORT', (int) ( getenv( 'APP_PG_PORT' ) ?: 6432 ) );
define( 'APP_PG_DB', getenv( 'APP_PG_DB' ) ?: 'appdb' );
define( 'APP_PG_USER', getenv( 'APP_PG_USER' ) ?: 'app_user' );
define( 'APP_PG_PASSWORD', getenv( 'APP_PG_PASSWORD' ) ?: '' );
<?php
// wp-content/mu-plugins/app-pg.php
function app_pg(): PDO {
	static $pdo = null;
	if ( $pdo instanceof PDO ) {
		return $pdo;
	}
	$dsn = sprintf(
		'pgsql:host=%s;port=%d;dbname=%s',
		APP_PG_HOST,
		APP_PG_PORT,
		APP_PG_DB
	);
	$pdo = new PDO( $dsn, APP_PG_USER, APP_PG_PASSWORD, array(
		PDO::ATTR_ERRMODE          => PDO::ERRMODE_EXCEPTION,
		// transaction モードの PgBouncer でも動くよう、準備済み文をエミュレートする
		PDO::ATTR_EMULATE_PREPARES => true,
	) );
	return $pdo;
}

function app_pg_get_order( int $order_id ): ?array {
	$stmt = app_pg()->prepare( 'SELECT id, status, total FROM orders WHERE id = :id' );
	$stmt->execute( array( ':id' => $order_id ) );
	$row = $stmt->fetch( PDO::FETCH_ASSOC );
	return $row ?: null;
}

エミュレーションを使っても、値はプレースホルダー経由で渡すため、SQL インジェクション対策としての効果は保たれます。ユーザー入力を SQL 文字列に直接連結しないことは、PgBouncer の有無に関係なく守ります。取得したデータを画面に出すときは、esc_html() などでエスケープしてください。

PgBouncer と PostgreSQL が別ホストにある場合は、DSN に sslmode=require などを加え、経路を暗号化することも検討します。

動作を確認する

PgBouncer には管理用の仮想データベース pgbouncer があり、admin_users に指定したユーザーで接続すると状態を確認できます。

psql -h 127.0.0.1 -p 6432 -U pgbouncer_admin pgbouncer
SHOW POOLS;
SHOW STATS;

SHOW POOLS の cl_waiting(空きを待っているクライアント数)が常に大きい場合は、default_pool_size が小さすぎるか、遅いクエリでサーバー接続が埋まっています。PostgreSQL 側の遅いクエリを先に調べ、それでも足りなければプールの大きさを見直します。

WordPress 本体の接続数を減らしたい場合

WordPress 本体(MySQL/MariaDB)の接続数が問題なら、次のような MySQL 向けの手段を検討します。いずれも機能や対応バージョンは更新されるため、導入前に各公式ドキュメントで確認してください。

  • PHP-FPM の同時実行数を見直す: 接続数はほぼ PHP の同時実行数で決まるため、DB の許容量に合わせて pm.max_children を調整します。最も手軽で副作用の少ない対策です。
  • キャッシュでクエリを減らす: ページキャッシュやオブジェクトキャッシュ(Redis など)で DB へのアクセスそのものを減らします。
  • ProxySQL: MySQL 向けのプロキシで、接続の多重化や読み書きの振り分けができます。
  • Amazon RDS Proxy: RDS や Aurora を使っている場合のマネージドな選択肢です。
  • MariaDB MaxScale: MariaDB 向けのプロキシです。

これらは PgBouncer と同じく、セッションの状態に依存する処理と相性の問題が起きることがあります。WordPress は接続時に文字コードや sql_mode を設定するため、導入時はステージングで管理画面の操作や投稿の保存まで試してください。

まとめ

  • PgBouncer は PostgreSQL 専用で、WordPress 本体が使う MySQL/MariaDB の接続には使えない。WordPress コアは PostgreSQL をサポートしていない
  • PgBouncer が役立つのは、プラグインや周辺アプリから別の PostgreSQL に接続する場面
  • PHP から使うなら transaction モードが一般的で、セッション変数や準備済み文の扱いに注意し、PDO では準備済み文のエミュレーションを使うと安全
  • 認証ファイルには SCRAM のハッシュを使い、接続情報は環境変数と wp-config.php の定数で管理する
  • WordPress 本体の接続数対策は、PHP-FPM の調整、キャッシュ、ProxySQL や RDS Proxy などの MySQL 向けの手段で考える

お気軽にご相談ください

お見積りへ お問い合わせへ
▲