# コネクションプーリング [[データベース接続モデル]]の「常時接続」を発展させ、接続の永続化に加えて**接続数の厳密な管理**を行う手法。JVMエコシステムで広く使われてきたが、プロキシ型の登場によりPerlなどのシングルスレッド言語でも実現できるようになった。 出典: [[@2015__yuuk.io__architecture-of-database-connection]]([[Yuuki Tsubouchi]]、2015年) ## 目的 データベースサーバ側には接続数の制限が複数層で設けられている: - **OS/カーネル制限**: ポート数上限(`ip_local_port_range`)・ファイルディスクリプタ数(`open_files_limit`)・`tcp_tw_reuse`(TIME_WAIT状態ポートの早期再利用) - **データベース層制限**: `max_connections`(PostgreSQL)などのユーザ設定値 これらを超えると接続を受け付けなくなる。コネクションプーリングは、クライアント側で接続数に上限を設けることで「多少の接続待ちが起きてもDBサーバを守る」仕組みだ。 ## 2種類の実装 ### ドライバ型(Driver-side Pooling) アプリケーションサーバ内のDBクライアントライブラリが接続をプーリングする。 **代表実装**: JDBC(JVM)の HikariCP・BoneCP など。スレッドプールとは別に接続プールを作成し、各スレッドのローカル変数に接続オブジェクトを持たせる構造が多い。 **特徴**: - JVMエコシステムが主戦場(ScalaはJVM上で動作するため自然に使える) - 1プロセス内で完結するため実装がシンプル - アプリサーバ台数に比例して接続数が増える(スケールアウト時の課題) **制約**: Perlのようにまともなスレッドがない言語・PreforkモデルのWebサーバでは、ワーカープロセス間でプールを共有するために専用プロセスが必要となり、ドライバ型では実現しにくい。 ### プロキシ型(Proxy-side Pooling) アプリケーションサーバとデータベースサーバのあいだに[[PgBouncer]]・[[Pgpool]]などの専用プロキシを挟み、プロキシ側でプーリングする。アプリ〜プロキシ間は都度接続でよい。 **メリット**: - 言語・アーキテクチャを問わず使える(Perlのようにスレッドがない言語でも利用可能) - 全アプリサーバからの接続数を一定に保ちやすい(アプリ台数増加時にDB接続数が増大しない) **デメリット**: - 管理層が増える(アプリ層 → プロキシ層 → DB層) - 層が増えると可用性がわずかに低下する - プロキシをアプリサーバに同居させれば台数は増えないが、接続数制御のメリットが薄れる ## チューニングパラメータ コネクションプーリング実装には多くのパラメータが存在する: | パラメータ | 説明 | |---|---| | 最大プール数 | DB側の制限に合わせて設定 | | 最小プール数 | ピーク時以外の余剰接続を抑える | | 接続タイムアウト | 取得待機の上限時間 | | 再接続時間 | 切断後の再試行間隔 | | 接続維持時間 | アイドル接続の破棄タイミング | **注意**: 最大=最小に設定すると、接続維持時間超過の瞬間に一斉再接続が発生し、プールから接続を取り出せないスレッドがエラーを吐く事故が起きる。BoneCPなどは最小・最大の範囲内で動的に接続数を変化させてメモリ効率を良くしている。 ## 導入判断 コネクションプーリングの実装に習熟するコストは低くない。以下の基準で判断するとよい: - DBのスケールアップ/アウトで解決できるなら、まずそちらを優先する - MySQLのマスタが接続数上限に近づいている場合 - 2〜3桁のMySQLスレーブ数を削減できる見込みがある場合 ## 横断的知見 - 今後の取り込みで、複数ソース間の関係を追記する。 ## 未解決の問い - この概念をどのソース群で継続的に検証するか。 ## 関連 - 上位概念: [[データベース接続モデル]] - 主要プロキシ実装: [[PgBouncer]] / [[Pgpool]] - 一次出典: [[@2015__yuuk.io__architecture-of-database-connection]]