コネクションプーリング
#DB #データベース
コネクションプーリングとは
アプリがDB操作をする度に毎回DB接続確立を行うのではなく、数個のDB接続(コネクション)を保持して使い回す方法。
前提として、アプリがDB操作する場合、以下の手順を踏む必要がある。
1. TCPコネクションの確立
2. DBコネクションの確立
3. DBユーザの認証
4. DB側でのプロセス/スレッド生成
5. SQL実行
6. DBコネクションの切断/破棄
7. TCPコネクションの切断/破棄
この「1, 2, 3, 4, 6, 7」の処理を毎回のごとくやってたのでは、アプリ側の処理速度の低下に繋がる。
よって、これらのオーバーヘッドを削減するためにコネクションプーリングが存在する。
コネクションプーリングの仕組み詳細
基本的なイメージは以下
DBとの接続オブジェクトをコネクションと定義する。
アプリは、コネクションプールというコネクションの保持場所から、待機中のコネクションをGETする。
もし待機中コネクションが存在しない場合、基本的戦略としてアプリは空きが出るまで待機 or Timeoutエラーになる
コネクションプールは、保持してるコネクション数が最大値でない場合、新規にコネクションを追加作成する
アプリは、待機中のコネクションがあれば、そのコネクションを使ってDBを操作する。
そしてDB操作が終われば、コネクションプールにコネクションを返す(解放する)。
q.icon アプリ側でコネクションオブジェクトを保持してる状態だとして、DB側ではコネクション保持をどう表現してるのか?
a.icon(おそらく)プロセス/スレッドを保持し続けてる、TCP接続を保持してる、そんなことをDB側でもしてるはず
q.icon DB側にはコネクション数の最大値とかあるんだろうか?
a.icon あることが大半。そのコネクション数から溢れると、DBがぶっ壊れることもあるらしい。
だから、アプリ側でコネクションを制限する意味があるんだとか。
基本的な実装パターンは2つに分かれる
1. ドライバーパターン
https://scrapbox.io/files/635e261ca1a17a001d214877.png
2. プロキシパターン
https://scrapbox.io/files/635e26740b14780021cfe0d0.png
コネクションプーリングのメリット
1.icon アプリからDBへの接続確立にオーバーヘッドが見られる場合、そのレイテンシ・リソースを削減できる
2.icon DB側に最大同時コネクション数がある場合、オーバーフローを事前に防げる
オーバーフローしたらDB自体がショートする恐れあるらしい。
結果、DB操作の処理時間が短くなる可能性があるよ
コネクションプーリングのデメリット
1.icon 常時接続のコネクション数だけ、DB・APサーバー両方のリソースを占有し続ける
2.icon 常時待機プロセス/スレッドが増える分、DB側のコンテキススイッチが増えて処理時間が長くなる
3.icon 「状態」を持つことによる不具合が起きる可能性がある
ここはまだよくわかんねぇonigiri.w2.icon
コネクションプーリングの利用基準
(正直ここはまだよくわからん...基本的には使っとけやって感じonigiri.w2.icon)
PostgreSQLは、接続確立のオーバーヘッドが大きいのでコネクションプーリング使っとけ
MySQLは、スレッドキャッシュでどうにかなるらしい
コネクションプールの適正サイズ
何の情報も無い状況の場合は...「APサーバーのCPUコア数 x 2 + (スピンドル数)」だけのサイジングを取っておけば良さげ
コネクションプールの適正サイズの求め方 - Qiita
About Pool Sizing · brettwooldridge/HikariCP Wiki · GitHub
この式は開始点でしか無いことに注意されたし。
この開始サイジングを起点に、プールサイズをチューニングするのが効率的なプールサイジング決定法となるだろう
Database Connections: Less is More | by Wahome | Medium
サイズは大き過ぎてもダメなんです
データベース接続数 - PostgreSQL wiki
参考
【Java】Webアプリケーションでのコネクションプーリング
Webシステムにおけるデータベース接続アーキテクチャ概論 - ゆううきブログ
コネクションプーリングとは?なぜ要るのか?を Oracle Database で簡単に説明してみる。 - Qiita
コネクションプーリング(コネクションプール)とは - 意味をわかりやすく - IT用語辞典 e-Words
DB接続の肝。コネクションプール無し・有りのイメージ
データベースアクセスの空白時間(2/2) - DBひとりでできるもん
RDBMSでコネクションプールが必要な理由、わからない。 - Togetter
コネクションプールサイジングでの適切な設定 - Ren's blog
コネクションプールの適正サイズの求め方 - Qiita
4.4.1 コネクションプーリング
切り取り線.icon
【Java】Webアプリケーションでのコネクションプーリング
コネクションプーリングとは、プログラムがデータベース(DBMS)へアクセスする際、アクセス要求のたびに接続や切断を繰り返すのではなく、一度確立したDB接続(コネクション)を維持して使い回す手法です。
これは直感的にわかってる知識onigiri.w2.icon
要するに、DBとの接続の必要がある度に新規接続確立を行うのではなく、すでに接続してるけど待ち状態の何かを使い回そうって話やなonigiri.w2.icon
これさ、イメージとしてはDB接続クライアント側でコネクションをプールするイメージなんやけど、その場合DB側ではどんな状態保持が行われてるんだろう???
q.icon コネクションプーリングの仕組みにおけるDB側の処理・状態はどうなってる?onigiri.w2.icon
a.icon DB側でもコネクション維持のための努力がなされてるはず
プロセス/スレッドを保持したり、コネクション情報を保持したりなど
ていうかこれは、コネクションプーリングのためというよりは、そもそもDB側がそういう仕組みになってるだけ
コネクションプーリングの仕組みが、そのDBの仕様に合わせて動いてるだけだ。
DBの接続をクライアント側から切らない or DBの自動切断時間に達しない限り、接続に関するリソースがDB側でずっと保持されてるんだと思うonigiri.w2.icon
どんな通信でも絶対に基礎的に必要な情報としてTCP接続があるな。これはいつ聞かれても答えれる状態になっておきたいなonigiri.w2.icon
todo.icon TCP接続について再勉強し、資料にまとめて抽象的に暗記しておくonigiri.w2.icon
このコネクションの確立や切断を行うたびに一定のオーバヘッド(処理負荷、間接的な処理時間)が生じるため、頻繁にアクセスが行われるシステムでは性能劣化の原因となってしまう場合があります。
TCPのスリーウェイハンドシェイクを毎回行ってたら、負荷がかかるやろアホンダラって話onigiri.w2.icon
このようにデータベースへのコネクションを使い回すことで、同時多数のアクセスが想定されるWebアプリケーションなどではコネクションが増えすぎてリソース不足に陥る事をコントロールできます。ただし、プールされたコネクションもメモリ容量などのリソースを占有(データベース側も含む)するため、用途や使用環境によって適切な設定値に調整する必要があります。
「(データベース側も含む)」が気になるなonigiri.w2.icon
コネクションプーリングしてる場合、やっぱりデータベース側のリソースも占有してる状況になるんやなonigiri.w2.icon
コネクションプーリングのサイズが大きくなればなるほど、クライアント・サーバー双方でリソース占有率が大きくなってくるって感じか。はいはい。
通常、アプリケーションからデータベースの情報を利用する場合は以下のような手順が必要です。
1. ネットワークコネクションの確立
2. 接続ユーザの認証
3. データベースコネクションの確立
4. SQLクエリの発行
5. SQLクエリの処理
6. SQLクエリの結果取得
7. データベースコネクションの切断
8. ネットワークコネクションの切断
抽象的に見るとこんな感じの流れか、確かにonigiri.w2.icon
「1.」がTCP接続の部分やな。UDP接続を使うDBとかあるんかな?
コネクションプールを使用する事で、1~3 、7~8 の処理が不要になり、データベースを使った処理の効率が良くなり、Webアクセスでのレスポンス向上も見込めます。
なるほどね。はいはいonigiri.w2.icon
まあ、でも確かに。1, 2, 3, 7, 8なんて、特殊な要求が無い限りは毎回同じことするよな
認証ユーザーが異なるとかいう稀な要求があったりするかもせんけど。それは稀や。
Webシステムにおけるデータベース接続アーキテクチャ概論 - ゆううきブログ
永続化やコネクションプーリングが必要かどうかは、クライアントとデータベースサーバの実装とデータベースに対するワークロード次第だと考えている。
へぇ、なるほど
データベースの実装とかも学ばないとダメか、確かにonigiri.w2.icon
ひとまず、今回は比較的多数のユーザから同時にアクセスされるWebシステムにおけるデータベース接続事情に話を限定したい。 PerlとJVMの世界、PostgreSQLとMySQLの世界の組み合わせを考えると、だいたいのケースは抑えられるのではないかと思う。
ほおほお、多数ユーザーから同時接続される世界での話における、効果的な接続方法を教えてくれるのかもonigiri.w2.icon
接続を考える上で、都度接続するというのは自然な発想だと思う。 その一方で、データベース接続の永続化とはなにか、なぜデータベース接続を永続化するのかということを改めて考えてみる。
データベース接続処理には一定のオーバヘッド(レイテンシ, CPU, メモリ消費)がある。 データベース、特にRDBMSの接続オーバヘッドとして、TCPコネクションを確立する(TCP 3-way handshake)ためのオーバヘッド、データベース層のハンドシェイクオーバヘッド、データベース層の認証オーバヘッド、データベースプロセス側の接続用プロセス/スレッド生成オーバヘッドなどがある。
おおおおおおonigiri.w2.icon
DBの違いにもよるが、接続には大きく4つのオーバーヘッドがあるんだな
1. ネットワーク層のTCPコネクション確立
2. DB層のハンドシェイク確立
3. DB層の認証処理
4. DBプロセス側の接続用プロセス/スレッド生成処理
もちろんデータベース製品により詳細は異なると思う。例えば、Redisはイベント駆動モデルのアーキテクチャなので、接続用のプロセスやスレッドを生成したりはしない。
だよねonigiri.w2.icon
TCPのフロー制御は、パケットロスしない限りは同時に送信するパケット数(ウィンドウサイズ)を徐々に増やしていくため、都度接続より常時接続のほうがネットワークスループットが大きくなりやすい。都度接続していると、ウィンドウサイズが初期サイズからのスタートになってしまう。
これは永続的に接続を保持してると、1回のデータ送信量が徐々に大きくなってくるってこと?onigiri.w2.icon
それはネットワーク通信のオーバーヘッドなのでは??
q.icon TCPのフロー制御ではパケット数が徐々に増えるの?
ちなみに、HTTPには前者のTCPの接続オーバヘッドは当然あるが、HTTP自体には接続確立のためのプロトコルはない。 あまりRDBMSのプロトコルに明るいわけではないが、この辺りから、RDBMSのプロトコルはもともと大量のクライアントからの接続を想定してはいなかったのではないかということが伺える。 接続ごとにプロセスやスレッドを生成しているところも、大量接続を意識してPreforkやスレッドプール、イベント駆動モデルを採用していることが多いWebサーバアーキテクチャの世界とは異なる。
へえええええええええええonigiri.w2.icon
おもろ、何この歴史
これらの接続オーバヘッドを節約する手法が、接続の永続化だ。接続の使い回し、接続のキャッシュといってもよい。 接続を使いまわすことにより、初回以外の接続確立のオーバヘッドを削減できる。
接続の永続化と一口に言っても、大きく2つの種類があると考えている。
Webアプリケーションにおける話ねonigiri.w2.icon
1つ目は、Webアプリケーションサーバのリクエスト処理中の間だけ、データベースとの接続を永続化するというものだ。 ここでは、これをリクエスト都度接続モデルと呼ぶことにする。
リクエストの度にDBの接続を確立するやり方やなonigiri.w2.icon
2つ目は、Webアプリケーションサーバのリクエスト処理に関わらず、常時接続を永続化するモデルだ。 これを常時接続モデルと呼ぶことにする。
これが、一般的にイメージされてるコネクションプーリングのことかもonigiri.w2.icon
接続のオーバヘッドを削減できる常時接続モデルのメリットが大きいようにみえる。 しかし、当然常時接続モデルにもデメリットはある。
まず、常時接続モデルはクライアント側のリソースリークなどのバグを作りやすい
次に、接続を永続化すると、LVS/HAProxyなどのL4ロードバランサを経由する場合、均等にバランスされない
さらに、接続の永続化により、データベースがフェイルオーバすると、アプリケーションサーバがフェイルオーバ先に再接続するまで、時間がかかることがある。
はいはい、よくわからんわwwwonigiri.w2.icon
q.icon常時接続(or コネクションプーリング)のデメリットってなんだ??
a.icon
管理が面倒になりがち
CPU・メモリなどの常時使用率上がる
など
データベースサーバ側の同時に接続できるコネクション数には様々な制限があり、これらの制限を超えてしまうと接続を受け付けなくなる。 クライアント側で接続数を管理できれば、データベース側の制限に引っかかなくてすむというわけだ。
やっぱりそうなん????onigiri.w2.icon
DB側にもコネクションの数に制限が設けられてるのか
てなるとさ、Webアプリを複数コンテナで管理してる時とかの、コネクション数管理って結構面倒くさそうじゃね?
q.icon Webアプリを複数コンテナ稼働させる場合のDBコネクションプール管理はどうなる?
a.icon プロキシタイプのプールを使った方が良さげ
APサーバーをスケーリングしやすい
コネクションプーリングの方法には主に2つのやり方がある
1. ドライバ型
JVMの世界のJDBCのようなアプリケーションサーバのデータベース接続クライアントが接続オブジェクトをプーリングするというもの
これをドライバ型と呼ぶことにしよう。 JDBCのコネクションプーリング(BoneCP、HikariCP など)は、大雑把には、リクエスト処理用のスレッドプール以外に、データベース接続用のスレッドプールを作成して、各スレッドのローカル変数に接続オブジェクトを持たせるような形になっている。
https://scrapbox.io/files/635e261ca1a17a001d214877.png
はいはい、なるほどねonigiri.w2.icon
アプリサーバー側でコネクションを保持していくのね。
ただ、これやとサーバーをスケーリングしにくいよなって思ったりするonigiri.w2.icon
2. プロキシ型
アプリケーションサーバとデータベースサーバの間にPgpoolやPgBouncerなどの接続管理のためのプロキシを挟んで、プロキシにプーリングさせるというもの
こちらは、プロキシ型と呼ぶことにする。 プロキシ型では、アプリケーションサーバとプロキシの間は都度接続でもよい。
https://scrapbox.io/files/635e26740b14780021cfe0d0.png
なるほどなるほどonigiri.w2.icon
ただ、これだとアプリとプロキシの都度接続の際にオーバーヘッドが発生するのでは??
q.icon プロキシ型のコネクションプーリングって結局、似たようなオーバーヘッドが発生するのではないか?onigiri.w2.icon
a.icon (おそらく)APとProxy間の新規接続は、AP-DBとの新規接続よりもオーバーヘッドが小さいんだと思われる。
それも無視できるくらい。ていうかそうなってないと意味ない。
補足だが、負荷のピークタイムが存在するシステムの場合、ピークタイムに合わせてプール数を設定していると、ピークタイム以外には余分な接続が開いたままなため、データベース側の接続維持のためのメモリが無駄になるという話もある。 BoneCPなどの一部のコネクションプールの実装は、最小接続数と最大接続数を設定しておき、最小と最大の定義域の範囲内で、接続数を負荷に合わせて動的に変更して、メモリ効率を良くしている。
負荷に合わせてコネクション数を増減させるような仕組みも考えられるのねonigiri.w2.icon
奥が深いなぁぁ
最近のWebサービスは大体がスケーリング前提で作られること多いから、Proxy型で作っておくのが楽だと思うな。AWSとかだとRDS Proxyていうフルマネージドサービスもあるしなonigiri.w2.icon
ドライバ型にせよプロキシ型にせよ、最大/最小プール数、接続のタイムアウト時間、再接続までの時間、接続を維持し続ける時間、などの多くのパラメータをチューニングしなければならないことがある。 もちろん、コネクションプーリングの実装次第で管理するパラメータは異なる。
奥が深いよなぁぁぁonigiri.w2.icon
PostgreSQLは1つの接続に対して、1つのプロセスを生成する。 つまり、マルチプロセスモデルであるため、後述のMySQLと比較して、メモリ消費量は多いといえる。 もちろん、Copy On Write(CoW)が効くはずなので、メモリ消費量は多少は最適化される。 しかし、接続を永続化していれば、徐々にfork元プロセスとの乖離が激しくなり、プロセスあたりのメモリ消費量は増加していく。 PostgreSQLは、接続を受け付ける度にforkするため、接続のオーバヘッドはそれなりに大きい。 大量接続を同時に受け付けると、負荷が跳ね上げることがある。
このようにPostgreSQLは、接続オーバヘッドが大きいことが知られているため、前述したPgpoolやPgbouncerなどのプロキシ型のコネクションプーリングと併用されることが多い
はぇえええええええええええonigiri.w2.icon
おもしろ、「PostgreSQLはマルチプロセスモデル」てとこが特に面白い。そうなんやonigiri.w2.icon
一方、MySQLは1つの接続に対して、1つのスレッドを生成する。 マルチスレッドモデルであるため、PostgreSQLと比べて、メモリ消費量は抑えられるはずだ。 とはいえ、スレッド生成/破棄にはそれなりのオーバヘッドがある。 MySQLにはThread Cacheの仕組みがあり、接続に使用したスレッドを使い回すことにより、オーバヘッドを抑えている。
MySQLでは、接続オーバヘッドが小さいことが知られているため、Mobage を支える Ruby の技術 ~ 複数DB編 ~ - sonots:blog に書かれているように、都度接続が用いられていることが多いようだ。
おもろおもろ、DBによってこんなに戦略が変わってくるんやなonigiri.w2.icon
こう思うと、プロセス・スレッドの基本、ネットワーク(TCP接続周りの基本)って本当に大事だと実感するわ
コネクションプーリングとは?なぜ要るのか?を Oracle Database で簡単に説明してみる。 - Qiita
コネクションプールによって発生する問題として無効接続が挙げられます。サーバープロセス側で問題が発生(プロセスのクラッシュやインスタンスダウン等)すると、そのサーバープロセスにクライアント側から SQL を実行するとハングしてしまいます(無効接続)。
これを回避するための方法が2つ
1. クライアント側からサーバー(DB)側にポーリングして、コネクションが有効かどうかを都度確認する
2. サーバー側からバグがあった時にクライアントに通知するようにして、クライアントは通知を受け取って行動に出る
まあ、ネットワーク上のやり取りとなったら、これのどっちかではあるよな。
「2.」の方が即効性が高いけど、実装は少し面倒くさそう
コネクションプーリング(コネクションプール)とは - 意味をわかりやすく - IT用語辞典 e-Words
この方式ではコネクション数の上限を設定できるため、多人数が一斉にアクセスする可能性のあるWebシステムなどではコネクションが増えすぎて負荷が高まることを回避することもできる。ただし、プールされたコネクションのためにもメモリ容量などの資源はある程度占有されるため、用途や使用環境によっては却って無駄が増える可能性もある。
DB側への大量コネクション確立によるサーバーダウンを抑えることができそうonigiri.w2.icon
ただし、コネクションを保持しておくことのリソース占有には気をつけてと。
データベースアクセスの空白時間(2/2) - DBひとりでできるもん
コネクションプーリングを用いれば、最初の1回だけ接続要求があるはずです。
リスナーログには大量の接続要求があり、また、毎回DBへのSESSION_IDが皮ていた。
このことからコネクションプーリングが使われてないと気づいた。
ほおほお、これはあれかなL4のログから確認したんかなぁonigiri.w2.icon
アプリケーションログじゃ確認できないよなこれって。はえええええonigiri.w2.icon
おもろ
RDBMSでコネクションプールが必要な理由、わからない。 - Togetter
RDBMSでコネクションプールが必要な理由、わからない。HTTPサーバで秒間数万リクエスト捌けるとかいう話を考えるとTCP/IPのセッションが重いというわけではないだろうし
確かに、これは確かにそう...か...???onigiri.w2.icon
q.icon TCPハンドシェイクってどれくらい重いんだろう?なぜHTTP接続では問題視されてないんだ?
コネクションプールが無い場合、使い終わったコネクションが即解放されない(解放まで多少遅延する)ので実際に使っているコネクションの数より多く存在する。その分メモリを圧迫して効率が悪い。っていう話は聞いたことがあるよ(要出典
はいはい。即切れって話ね
ここを読んでる感じだと...TCP接続の確立自体はそこまでオーバーヘッドになってない説ありそうonigiri.w2.icon
それよりもDB側で、プロセスの生成/破棄を繰り返してるのが厳しいのかもしれない
あと、無思考にコネクションプーリングを使うのも危ないって。
まあ、ここはどの分野にも言えることやけどww
MySQLとかだと、コネクションプーリングなしでも、そこまでオーバーヘッドがないとかなんとか...
RDBMSだと、プロセス生成とDBの認証まわりだけで、普通に無視できないレベルで重い処理だよね。同時に数百コネクションを同時に繋いだり切ったりして、DB側の負荷を見ると普通に跳ねる。
なるほどなぁonigiri.w2.icon
TCPはやっぱりそこまで大きな問題じゃなさそうやな
結局は、しっかりモニタリングしてチューニングしていくのが大事にはなるonigiri.w2.icon
これも当たり前の話やけども...w
接続プーリングとは何か、なぜ注意する必要があるのか
残念ながら、ここには簡単な答えはありません。接続プールの最適なサイズを決定するには、ブログ投稿では取り上げられない多くの固有のアプリケーションと実装の詳細を考慮に入れる必要があります。
なるほどなぁ...onigiri.w2.icon
ベンチマークとかで調べ上げて都度最適なサイジングを探すしかないんかなぁ...