Hatena::ブログ(Diary)

naoyaのはてなダイアリー

September 12, 2006

コネクションプーリングの話

かなりながーいエントリになる予定なので,結論だけ最初に書くとこんな感じ.

最速配信研究会 - 「コネクションプーリング都市伝説」はほんとに都市伝説?(その1)

この話題については自分も あとで書く と言って書いてなかったので書いてみますよ。2006年の下期にもなってコネクションプーリングかよというツッコミもありそうですが、あとで書くといったら書くの。あとで読むといったら読む。

普通「コネクションプーリング」と言ったら、主に二つの役割があると思います。話を簡単にするためにウェブアプリケーションに限定して言及します。

  • ウェブアプリケーションから DB への接続を開けっ放しにして、接続に必要とされるオーバーヘッドをカットして双方の負荷を下げる。
  • ウェブアプリケーションと DB への接続を「使いまわす」ことで、同時接続本数を節約する。

というもの。

mod_perl で DB と接続維持するとコネクション数増えて云々という話は主に前者のみについての話になります。Apache::DBI や Class::DBI の接続維持の実装は割と簡素なもので、各ウェブサーバーのプロセスごとにデータベースへの接続をメモリ内で保持して解放しないでおいて、次のリクエストでもそれを使いまわすだけ。そうすると1プロセスの中での接続は共有できて節約できるものの、同時接続数はウェブサーバのプロセス数に等しくなるので、サーバーがたくさんある環境では接続数が多くなりすぎて DB 側のリソース(主にメモリ)を消費してしまう。

でもどうも MySQL は thread_cache とかを有効にすると(案外しなくてもそうなんだけど)実は接続に必要なオーバーヘッドがアプリケーション総体では無視できる程度の微々たるものだということがベンチマークの結果とかで分かる。それだったら Apache::DBI や CDBI の永続化の機能は OFF にしちゃいましょう...これがコネクションプーリングイラネ、みたいな話。

話は変わって、役割の二つ目。

PostgreSQL にコネクションプーリングの機能を追加する proxy である pgpool の実装なんかが分かりやすいと思います。クライアントからDBへのコネクションを pgpool が肩代わりして、接続を pgpool が管理し、DB 間との接続を空いているものをクライアントに渡す、みたいなことをして接続本数を節約したりというやつ。

この辺どういう仕組みで接続を管理するかは実装次第なので、pgpool のようなサーバー型のようなものもあるでしょうし、アプリケーションサーバでスレッドや共有メモリで共有してみたりというのもあるかもしれません。

私見なんですが「コネクションプーリング」の「プーリング」っていうのは、主に後者あるいは両方を含めたモノのことを指す言葉なんじゃないかなあと思うんですよ。普通コンピューターの世界でプーリングといったら単純な永続化じゃなくて、どこか共有のスペースにオブジェクトなり何なりを放り込んで(プールして)おいて、必要になったらそこから取り出す、要らなくなったらそこに戻す、それでオブジェクトの数を節約しましょうという類のものじゃないかなと思うのですが。実際、Class::DBI や Apache::DBI のドキュメントには "Persistent" とはありますが "Pooling" という言葉は使われていませんし。(同一プロセスで DBI で複数の接続を開いた場合にそれを一本にしてくれる、という意味では確かに共有ですが、それは pool というより share なような気がします。)

コネクションプーリングという言葉をよく目にするようになったのは、4、5年くらい前の J2EE 全盛の頃で、やはり Java の世界でよく使われていたような気がします。Java はそんなに詳しくないんであれなんですが、当時コネクションプーリングの実装方法はいろいろあって、Commons の DBCP のような実装とか、アプリケーションサーバーが面倒見てくれるとか色々でした。その中には簡易な実装の、Apache::DBI がやってるのと同じようなことをしているものもあるかもしれません。が、一般的には Java の世界でコネクションプールといったら、接続用のオブジェクトをどこかで保持して使いまわすというものでした。(なので、プールの最大数をいくつにするかというので頭を悩ませていた気がします) 最近はどうなのかは分かりません。

それで、Apache::DBI や Class::DBI (の一部の機能)を OFF にするというのは "コネクションプーリング" をオフにしたというより、接続の永続化をオフにしたのであって、Perl あるいは MySQL に接続をプールするという意味でのコネクションプールな優れた実装があるのであれば OFF にする必要はないのではないかと思います。つまり、Perl の世界でのあれは一般的な意味でのコネクションプーリングの議論をしてるのではなく、永続化の話だけしているという。

加えて LL + MySQL でコネクションプーリングというとちょっと待て、というのが話をややこしくしてるんじゃないかなと。

LL なウェブアプリケーションの世界で使われる MySQL とそのバインディングの実装が、コネクションを毎回開いて閉じてとしてたら重くてかなわん! というものだったら LL の世界でもプーリングの実装が重宝された*1 のでしょうが、実際使ってみると全然支配的じゃないと。むしろ適当にチューニングしてやれば無視できてしまう程度のもので。じゃあ永続化も共有もいらない、せいぜい1リクエスト内でハンドラ共有しとけばいいじゃないという塩梅です。そんな感じだったので、Perl を含む特定の LL の世界では「コネクションプーリング = 接続の永続化のみ」として受け取られてるんじゃないかな。

ということで、コネクションプーリングが要らないというのが話に上るのは、上記のような過程や背景、環境があるからであって、ウェブアプリケーション全部の世界でそれが言えるかというとそうではないというのが僕の見解。なのでやはり「コネクションプーリング都市伝説」という言葉をそのまま受け取ってしまってはいけない。多くの環境ではやっぱりコネクションプールは有用なテクニックだと思います。

長くなってきましたのでこの辺で。続きは id:yamaz さんが書いてくれるそうです。たぶんw

*1:MySQL でも使えそうな、本当の意味でのプーリングの実装ということで SQLrelay というのを恥ずかしながら初めて聞いたのですが、どうもいろいろ見てみると、単に繋げばいいというわけじゃなくて制約もあるようで、そんな気軽なものでもなさそうでした。

charchar 2006/09/13 12:49 私はJAVAの方の人なので、はずしているかもしれませんが、
「使いまわす」ことは、何もDB接続に限らず、「オブジェクトプーリング」として、今やデザインパターンの1つとして認識されている方も多いです。
naoyaさんのおっしゃるとおり、非常に有用なテクニックだと思います。
「ただのキャッシュじゃん」という声もありますが。
あと、シングルトンにも似ていると思います(プールの中のオブジェクトが常に1つならシングルトン)。
こういった似てて非なるものをシステム要件に沿って使い分けることが設計の妙であって、一面的なベンチマークだけで否とするのは納得いかないです(もちろん要件によってはプーリングを使わないという選択もある)。

tociyukitociyuki 2006/09/15 13:37 横槍ですが、char さんのオブジェクトプーリングは、naoya さんが書いている「thread_cache とかを有効にする」に相当します。
一方、コネクション・プーリングは、サーバまたはクライアントのどちらかのオブジェクトの内部状態遷移のオーバーヘッドが、システムに求められる応答時間の要求を満たしていないので、強制的に固定してやる手段です。

tttt 2006/09/17 23:50 アーキテクチャ(CPU)やDB構成(localか外DBかレプリケーションしてるとか)の違いで有用性が変わってくるでしょう。また、リクエストがある程度一定か、一時的に大量に来るかでも違いがあると思われます。その状況によってまちまちでしょうから都市伝説と言い切れない気がします。