TECH BLOG
技術ブログ
  • 2023-12-01 PostgreSQLPostgreSQL パラメータ

    max_connections に最大値を設定するとどうなるのか

max_connections について

PostgreSQL では、サーバプロセスに接続できるクライアントプロセスの上限を max_connections というパラメータで制御しています。
例えば max_connections が 10 で、すでに 10 個のクライアントプロセスが接続している場合に 11 個目のクライアントプロセスが接続しようとしても

FATAL: sorry, too many clients already

というエラーが発生し、接続が拒否されます。

上記のとおり、max_connections の主な用途としては、接続できるクライアントプロセスの上限を設定することになります。
max_connections を変更するには、PostgreSQL の再起動が必要になります。
そのため、あらかじめピーク時を想定した値を設定しておくことが推奨となります(可能であれば、ある程度の裕度を持たせた値にしておくと安心です)。

max_connections に設定する値

max_connections に設定できる最大値は 262143 ですので「とりあえず 262143 をしておけばよいのでは」と安易に考えたくもなりますが、最大値(に近い値)を設定すると処理性能によくない影響を与える可能性があります。

もちろん max_connections を大きくしたからといって、その値に比例してシステムリソースが消費されるわけではありませんが、トランザクションのロック競合に関連するテーブルのサイズが max_connections に影響を受けるようです。

公式ドキュメントの該当する記述を下記に引用します。

【 PostgreSQL 15.4文書 - 20.12. ロック管理 - 】
https://www.postgresql.jp/document/15/html/runtime-config-locks.html

共有ロックテーブルは、max_locks_per_transaction * (max_connections + max_prepared_transactions)オブジェクト(例えばテーブル)上のロック追跡します。

実際にソースコードの該当箇所を探したところ、backend/utils/init/predicate.c の initPredicateLocks 関数で行われているループ処理の繰り返し数が max_connections の影響を受けていることを確認できました。
該当の記述を抜粋します。

predicate.c から抜粋(PostgreSQL 15.3)
        memset(PredXact->element, 0, requestSize);
        for (i = 0; i < max_table_size; i++)
        {
            LWLockInitialize(&PredXact->element[i].sxact.perXactPredicateListLock,
                             LWTRANCHE_PER_XACT_PREDICATE_LIST);
            SHMQueueInsertBefore(&(PredXact->availableList),
                                 &(PredXact->element[i].link));
        }

上記の for 文内の max_table_size が max_connections の影響を受けています。
max_connections が大きくなればなるほどループの回数も増えて、その結果、処理性能への影響が発生する、という状況が予想されます。

では、具体的に max_connections の値によって、どの程度の性能差が発生するのかをpgbench を利用して確認してみました。
環境は

  • Ubuntu 22.04
  • PostgreSQL 15.3

です。

pgbench の結果

pgbench の -c オプションで並列数 1, 5, 10, 20, 30 の 5 パターン計測しました。
縦軸が tps で、横軸が max_connections です。

グラフから分かること
  • 並列数が 1, 5 のときは、max_connections の影響はなさそうです。
  • 並列数が 10 以上のとき、max_connections が 10,000 を超えたあたりから tps が下がっています。
    • tps は小さいほど性能がよくないです。

実際の運用では、大規模なシステムでも max_connections = 2000 以上になることは稀だとは思いますので、ピーク時にある程度の余裕を持たせた値を設定しても、性能への影響は小さいと考えられます。

max_connections を大きくしすぎるとエラーが発生する

ここで、そもそもの話となってしまうのですが、サーバ(OS)のリソースと比較して max_connections に大きな値を設定してしまうとエラーが発生します。
そのため、サーバのリソースが潤沢でないと max_connections に最大値に近い値を設定することはできません。
手元の検証環境では max_connections を 15,000 にすると下記のようなエラーが発生しました。

FATAL: could not map anonymous shared memory: Cannot allocate memory
2023-07-17 09:05:55.831 JST [128381] HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages. To reduce the request size (currently 7665147904 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.

ちなみに上記のエラーは max_connections だけでなく shared_buffers を大きくし過ぎた場合にも発生する可能性があります。

まとめ

「max_connections に最大値(に近い値)を設定するとどうなるのか」という疑問については

  • ある程度の負荷が発生している状況では、処理性能が落ちる
  • サーバ(OS)のリソースが足りていない場合、エラーとなる

ということが、今回の検証から確認できました。
ただ、今回の検証で利用した検証環境のリソース不足のため、最大値の半分以下程度での検証となってしまいました。
今後、機会があればリソースが潤沢な検証環境を利用して、改めて max_connections を最大値にした場合の性能を確認したいと思います。

CATEGORY

ARCHIVE

PostgreSQLに関するご相談は
株式会社インサイトまで
お気軽にお問い合わせください。

CONTACT