PgBouncer の設定
この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。
最終更新日 2023年05月16日(火)
Table of Contents
Heroku Postgres ユーザーで、選択されているデータベースプランの接続制限が発生する場合があります。その結果、接続リクエストがキューに入れられるため、パフォーマンスや信頼性が低下する可能性があります。PgBouncer で接続プールを使用すると、クライアント間でデータベースサーバー接続を共有することによってこの問題を解決できます。
この記事の内容は次のとおりです。
- データベース接続とセッションの動作の概要について説明します。
- PgBouncer で使用可能な接続プールのさまざまなタイプについて詳細に説明します。
- Heroku で PgBouncer を使用した接続プールを実装する方法について説明します。
Premier または Signature Success Plan の Heroku Enterprise の顧客は、Customer Solutions Architecture (CSA) チームに、このトピックに関する詳細なガイダンスを要求できます。ここでエキスパートコーチングセッションについて学習するか、または Salesforce の担当者にお問い合わせください。
データベース接続、セッション、接続プール
接続プールを使用すると、データベース接続とセッションの動作に関する重要な変更が導入されます。これらの変更を理解するには、まず、それらの通常の動作を理解することが重要です。プールされていない接続は、次の標準のクライアントサーバー接続アーキテクチャに従います。
接続プールを使用しない PostgreSQL 接続のライフサイクルの高レベルのビューを次に示します。
- クライアントが、サーバーへの接続を依頼し、それを認証することによって新しいセッションを開始します。
- サーバーは、接続と作業セッションを処理するための新しいシステムプロセスをフォークします。このセッションの状態は、サーバーレベル、データベースレベル、およびユーザーレベルの設定パラメータの組み合わせごとに初期化されます。
- クライアントは 1 つ以上のトランザクションを実行することによって、必要な量の作業を行います。例として、次のものがあります。
- 関連 (テーブルやビューなど) に対する読み取りや書き込みを実行する
- SET コマンドを使用して、セッションまたはトランザクションの状態を変更する
- プリペアドステートメントを準備して実行する
- クライアントが接続を切断すると、セッションは終了します。
- サーバーは、セッションを処理したプロセスを破棄します。
データベースセッションは、1 つの接続の有効期間にわたって実行されたすべての作業で構成されます。データベースセッションは時間の長さが可変であり、クライアントとサーバーの両方で可変量のリソースを消費します。
これの重要な点は次のとおりです。
- 接続プロセスの作成、管理、および破棄は時間がかかり、リソースを消費します。
- サーバーの接続数が増えると、それらの接続を管理するために必要なリソースも増加します。さらに、クライアントがそれらの接続で作業を行うと、サーバーのプロセスごとのメモリ使用量は増加し続けます。
- 1 つのセッションは 1 つのクライアントしか処理しないため、クライアントはデータベースセッションの状態を変更し、これらの変更が連続したトランザクションにまたがって保持されることを期待できます。
接続プーラーは、クライアントとサーバーの間に配置されます。クライアントはプーラーに接続し、プーラーがサーバーに接続します。接続プーラーの導入によって、接続モデルがクライアントプロキシサーバーアーキテクチャに変更されます。
これにより、クライアント接続の有効期間がサーバー接続とプロセスの有効期間から切り離されます。接続プーラーは、次の役割を担うようになります。
- クライアントからの接続の受け付けと管理
- サーバーへの接続の確立と保持
- クライアント接続へのサーバー接続の割り当て
これにより、次のことが可能になります。
- 1 つのサーバー接続で、異なるクライアントのセッション、トランザクション、ステートメントを処理できます。
- 1 つのクライアントセッションのトランザクションかステートメント、またはその両方を異なるサーバー接続で実行できます。
この記事の残りの部分は次のとおりです。
- “クライアント接続” は、クライアントと接続プーラーの間の接続を指します。
- “サーバー接続” は、接続プーラーとサーバーの間の接続を指します。
PgBouncer の接続プールのモード
PgBouncer には、使用可能なプーリングモードとしてトランザクションプーリング、セッションプーリング、ステートメントプーリングの 3 つがあります。それぞれの動作を理解することが重要です。使用されるプーリングモードは、次のように機能します。
- サーバー接続がクライアント接続にどれだけ長く割り当てられたままになるかを決定します。
- クライアントは何ができ、何ができないかに関する制限を課します。これは、次のセクションで説明されます。
トランザクションプーリングモード (推奨)
データベースクライアントが、連続したトランザクションを間で一時停止せずに実行することはめったにありません。代わりに、データベース以外の作業はトランザクション間で実行されます。つまり、サーバー接続は新しい作業の到着を待機している間、アイドル状態で長い時間を費やします。
トランザクションプーリングモードは、次のように、サーバー接続のアイドル時間を短縮しようとします。
- クライアントがトランザクションを開始すると、プーラーはそのクライアントにサーバー接続を割り当てます。
- クライアントのトランザクションが完了するとすぐに、プーラーは接続の割り当てを解放します。
これは次のことを示します。
- クライアントが複数のトランザクションを実行する場合は、各トランザクションが異なるサーバー接続で実行される可能性があります。
- 1 つのサーバー接続が、その有効期間中、異なるクライアントによって発行されたトランザクションを実行できます。
これにより、アクティブなクライアントの数は、サーバーによって許可される接続よりはるかに多くなります。特定のワークロードに左右されるものの、アクティブなクライアント接続のサーバー接続に対する比率が 10 倍以上になることも珍しくありません。
これには、次の重要な注意事項が伴います。同じクライアントによって作成された連続したトランザクションが異なるサーバー接続で実行される可能性があるため、クライアントは、データベースセッション状態への変更がそれらのトランザクションにまたがって保持されることを期待できなくなります。さらに、クライアントがセッション状態を変更すると、それが他のクライアントに影響を与える可能性があります。
トランザクションプーリングを示す上の図の例を使用したいくつかの例を次に示します。
- クライアント 1 が T1 で最初のサーバー接続でセッションを読み取り専用に設定し、クライアント 2 の T3 が書き込みトランザクションである場合、読み取り専用になっているサーバー接続で実行されるため T3 は失敗します。
- クライアント 1 が T1 で
PREPARE a1 AS ...
を実行した後、T2 でEXECUTE a1 ...
を実行した場合、そのプリペアドステートメントは T1 が実行されたサーバー接続に対してローカルであるため T2 は失敗します。 - クライアント 2 が T3 で一時テーブルを作成し、T4 でそれを使用しようとする場合、その一時テーブルは T3 が実行されたサーバー接続に対してローカルであるため T4 は失敗します。
トランザクションプーリングモードの利点:
- アクティブなクライアントの数が、サーバーによって許可される接続より多くなります。
- 特定の数のクライアントに必要なサーバーリソースが削減されます。
トランザクションプーリングモードの注意事項:
SET
を使用したセッション状態に対するすべての変更は、その変更が現在実行されているトランザクションにのみスコープ指定されるように、SET LOCAL
を使用してのみ行う必要があります。SET SESSION
、またはトランザクションプーリングではデフォルトでSET SESSION
になる単独のSET
を決して使用しないでください。- プリペアドステートメントは使用できません。
- 一時テーブルを使用している場合、それらのテーブルは同じトランザクションで作成、使用、および削除する必要があります。ヒント: 一時テーブルを作成するときに
ON COMMIT DROP
を使用すると、作成したトランザクションが完了したとき、それらのテーブルは自動的に削除されます。 options
などの特定の接続パラメータを PgBouncer に渡すことはできません。
トランザクションプーリングを使用するときにサポートされないセッション状態の機能と操作の完全な一覧については、PgBouncer の一覧を参照してください。
セッションプーリングモード
ここでは、クライアントへのサーバー接続の割り当てがクライアント接続の有効期間中は継続されます。これは、接続プーラーをまったく使用しない場合と同じに見えるかもしれませんが、重要な違いがあります。割り当てられているクライアントが接続を切断しても、サーバー接続は破棄されません。クライアントが接続を切断すると、プーラーは次の処理を行います。
- そのクライアントによって行われたセッション状態の変更をすべて消去します。
- サーバー接続を別のクライアントで使用できるようにプールに戻します。
セッションプーリングモードの利点:
- セッションプーリングは、クライアントが接続したとき、サーバーが新しい接続プロセスを作成するまで待機するために費やされる時間を短縮します。
- 多くの ORM やアプリケーションフレームワークが、その組み込みの接続プールによるセッションプーリングを提供しています (Ruby on Rails など)。
セッションプーリングモードの注意事項:
- サーバー接続の割り当てが、割り当てられているクライアント接続の有効期間中は継続されるため、アクティブなクライアント接続の数が依然としてサーバーの接続制限によって制限されます。
- セッションプーリングで PgBouncer を使用する前に、選択したアプリケーションフレームワークか ORM、またはその両方にセッションプールが用意されているかどうかを必ず確認してください。
options
などの特定の接続パラメータを PgBouncer に渡すことはできません。
ステートメントプーリングモード
ここでは、サーバー接続の割り当てが 1 つのステートメントの期間中しか継続されません。これには、トランザクションプーリングモードと同じセッション状態の制限がありますが、さらにトランザクションのセマンティックスも中断されます。
これにより、すべてのクライアント接続が “自動コミット” モードであるかのように動作します。クライアントがマルチステートメントトランザクションを開始しようとすると、プーラーはエラーを返します。それは制限になりますが、アクティブなクライアント接続の数はトランザクションプーリングの場合より多くなります。適切なユースケースとしては、大量の単純なキールックアップの処理や、単一ステートメントの書き込みの発行などがあります。
ステートメントプーリングモードの利点:
アクティブなクライアント接続の数が、トランザクションプーリングモードよりはるかに多くなります。
ステートメントプーリングモードの注意事項:
- トランザクションプーリングモードと同じセッション状態の制限があります。
- マルチステートメントトランザクションは許可されません。
options
などの特定の接続パラメータを PgBouncer に渡すことはできません。
Heroku での PgBouncer: サーバー側とクライアント側の比較
Heroku プラットフォームで PgBouncer を使用するためのオプションには、サーバー側とクライアント側の 2 つがあります。各オプションの主な機能を次に示します。
サーバー側
- Heroku Postgres サーバー上でローカルに動作する
- トランザクションプーリングモードのみ
- 最大 10,000 のクライアント接続をサポート
- ユーザーの設定変更はサポートされない
- デフォルトの Postgres 資格情報での使用のみをサポート
- このオプションを設定して使用する方法については、ここのドキュメントを参照してください。
クライアント側の buildpack
- 使用が (アプリケーションの Procfile で) 設定されている各 dyno タイプでローカルに動作する
- アプリケーションの環境設定を使用した PgBouncer の最も一般的な設定オプションの設定が可能
- 任意の数の Heroku Postgres 資格情報で動作する
- buildpack をインストールして使用する方法については、ここのドキュメントを参照してください。
サーバー側またはクライアント側のどちらかの PgBouncer の選択
サーバー側のオプションは、設定項目の少なさが制限になるように見えるかもしれませんが、使いやすいトランザクションプーラーを必要とする最も一般的なユースケースに対応しています。ある時点でその制限が厳しくなりすぎた場合は、代わりにクライアント側の buildpack を使用できます。
次の場合は、まずサーバー側のオプションを検討してください。
- トランザクションプーリングモードをまさに必要としている。
- デフォルトの Heroku Postgres 資格情報のみを使用している。
次の場合は、クライアント側のオプションを使用してください。
- 接続プールの制御と設定可能性を完全なものにしたい。
- デフォルトの資格情報だけでなく、それ以上の資格情報を使用する。
- セッションプーリングモードまたはステートメントプーリングモードのどちらかが必要である。
クライアント側の buildpack の設定
デフォルト値から変更することを考慮する必要がある主な設定オプションの簡単な概要を次に示します。
PgBouncer の接続プールは、1 人のデータベースユーザーによって 1 つのホスト上の 1 つのデータベースに対して作成されたすべての接続で構成されています。たとえば、'user1’ によって ‘host1’ 上の ‘database1’ に対して作成されたすべての接続が、特定の PgBouncer インスタンス上の同じプールを使用します。
PGBOUNCER_POOL_MODE (Default: transaction)
これは、セッションプーリングモードまたはステートメントプーリングを使用する場合に変更します。
PGBOUNCER_URLS (Default: DATABASE_URL)
各 dyno 上のローカルの PgBouncer インスタンスを指すように書き換えるための、アプリケーション設定 URL の (スペースで区切られた) 一覧。この URL の書き換えは、アプリケーションの環境設定ではなく、実行中の dyno 上の環境変数値にのみ影響を与えます。
PGBOUNCER_MAX_CLIENT_CONN (Default: 100)
プーラーが完全に拒否するようになる前に、管理されているすべてのプールにまたがって受け入れるクライアントの数。トランザクションまたはステートメントプーリングを使用しているときは、サーバー接続の制限よりはるかに多くなる場合があるため、デフォルト値が大きくなっています。
PGBOUNCER_DEFAULT_POOL_SIZE (Default: 1)
予約接続 (以下を参照) を使用する前にプールに割り当てることができるサーバー接続の最大数。dyno ごとに並列リクエストを処理する大きな、トラフィック量の多いアプリケーションでは、この値を増やします。
この値を設定する場合は、すべての dyno 上のすべてのプールにわたって許可されるサーバー接続の合計数がサーバーの接続制限を超えることがないように注意してください。
たとえば、それぞれに 1 つのプールを持つ 20 の dyno を実行することを計画している場合、20 のすべての dyno にまたがって作成できるサーバー接続の最大数を決定するには、この値に 20 を掛ける必要があります。その計画で 500 の接続を許可する場合は、この値を 25 以下に維持する必要があります (20 * 25 -> 500)。
PgBouncer での Preboot 機能の使用に関する注意事項
Preboot は、dyno の置き換えを起動してから、置き換えられる dyno を停止する (dyno の再起動は dyno の置き換え) ことによって、dyno の再起動のトラフィックの中断への影響を最小限に抑えるように動作します。 すべてのプールの dyno にまたがって許可されるサーバー接続の合計数が Heroku Postgres プランの接続制限の半分を超えており、かつ新しい dyno が急速に増大した場合は、アプリケーションのリクエストがデータベース接続を待機している間に停止し、これに対処するためにプールサイズの削減が必要になるという問題が発生する可能性があります。
PGBOUNCER_RESERVE_POOL_SIZE (Default: 1)
および PGBOUNCER_RESERVE_POOL_TIMEOUT (Default: 5 seconds)
特定のプールのアクティブなサーバー接続が現在最大値に達している (PGBOUNCER_DEFAULT_POOL_SIZE
を参照) ために、クライアントがサーバー接続の割り当てを待機している場合は、PGBOUNCER_RESERVE_POOL_TIMEOUT
の待機時間が経過した後に最大 PGBOUNCER_RESERVE_POOL_SIZE
個の追加の接続を許可します。予約プールは、すべての接続プールで共有されます。
PGBOUNCER_SERVER_IDLE_TIMEOUT (Default: 10 seconds)
これより長い時間にわたって未使用のままになっているサーバー接続はすべて閉じられます。これを 0 に設定すると、アイドルタイムアウトが無効になります。
PGBOUNCER_SERVER_LIFETIME (Default: 3600 seconds)
この設定より長い時間にわたって有効になっている未使用のサーバー接続はすべて閉じられます。これを 0 に設定しても、この設定は無効にならず、代わりに各サーバー接続が 1 回使用の接続になります。
PGBOUNCER_SERVER_RESET_QUERY (Default: "DISCARD ALL;" in session pooling mode, empty otherwise)
これは、クライアントがセッションプーリングモードで接続を切断した後、プーラーがセッションをリセットするために使用します。この記事の前の部分で詳細に説明されているように、トランザクションおよびステートメントプーリングモードを使用しているときは、これを使用してサポートされないセッション状態の機能を回避するための効果的な方法はありません。
PGBOUNCER_QUERY_WAIT_TIMEOUT (Default: 120 seconds)
これにより、プーラーがエラーを返す前に、クライアントがサーバー接続の割り当てを待機するために許可される時間が決定されます。クライアントが定期的にサーバー接続を待機していることが疑われる場合は、これを小さい値にすると、最終的に続行したクライアントではなく、失敗したクライアントが原因であることを示すのに役立ちます。