ブログトップ 記事一覧 ログイン 無料ブログ開設

wyukawa’s blog

2013-11-16

RDBMSのコネクションプーリングとかその辺の話

これは素晴らしい資料で後半のキャリアの話とか面白いんだけど、今回書くのはp6,p8に書かれていた下記の話です。

  • PosgreSQLは接続がプロセスベースなのでLL言語との相性がよくない
  • Pgpool(これはプロキシサーバー的に使うらしい)などのコネクションプールと併用することが多い
  • MySQLは接続がスレッドベースなのでコネクションプーリングが使いづらいLL言語環境では魅力

なんでLL言語だとコネクションプーリングが使いづらいのかわからずつぶやいたらリプライもらってついでにちょっと前に話題になったRDBMSでコネクションプールが必要な理由、わからない。 - Togetterまとめや7年前のブログエントリである コネクションプーリングの話 - naoyaのはてなダイアリーを読み返してみて思ったことを書いてみる。全然まとまってないし正確性も全く保証できないけど書いてみる。


言語や実装、RDBMSの種類を明示して書いた方がいいと思ったので、まず僕が一番なじみのあるJavaから書きます。


JavaでコネクションプールっていうとDBCPとかAPサーバーに付属しているやつのことで、役割としてはRDBMSとのコネクションをあらかじめ一定数保持しておいてリクエストごとに使い回すというやつ。実装レベルの話でいうと、JavaのWebアプリは大半がServletベースのものだと思うのでHTTPリクエストはスレッドに対応する。スレッド単位にコネクションを割り当てて使う感じ。なのでThreadLocal使ってコネクションプールは実装されていると思う。


Perlは門外漢だけど上記ブログによると下記のように書かれています。

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


・ウェブアプリケーションから DB への接続を開けっ放しにして、接続に必要とされるオーバーヘッドをカットして双方の負荷を下げる。

・ウェブアプリケーションと DB への接続を「使いまわす」ことで、同時接続本数を節約する。

というもの。


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


(中略)


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


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


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

なるほど。Javaの世界と違いますね。

DBへのコネクションを張りっぱなしにするだけで別にプールするわけではない。

mod_perlだとHTTPリクエストはプロセス単位だと思いますが、Webサーバーがたくさんある環境なら大量のコネクションがDBに張られることになるので張りっぱなしにするのは現実的ではない。よって接続がスレッドベースでプロセスよりコストが軽いMySQLが使われていると。

あとJavaの世界でいうコネクションプールがLL言語だと一般的でないのはプールは排他制御が必要だけどRuby/PythonだとGILがあったりでスレッドがメジャーじゃないとかもあるのかなと。

PostgreSQL使ってるInstagramではPgBouncerというコネクションプールを使っているようだけど、これはどんな感じなんだろ。

コネクションプールに PgBouncer を利用 (これがパフォーマンス向上にかなり効いたらしい)。

Kosei Kitahara’s Blog: Instagram のスケール正攻法

コネクションプールから離れてPythonのDBドライバの話題でいうと、PinterestはMySQL-pythonではなくgeventモンキーパッチができるpymysqlに切り替えたらしい。

geventには、yieldするために、socket / thread / time.sleepなどコモンライブラリがブロックするのを修正するモンキーパッチユーティリティがある。しかし、例えば外部のCライブラリに依存しているライブラリ (e.g. MySQL-python, pylibmc, zc.zk) がブロックする場合は対応できないので、Pythonのインプリ (e.g. pymysql, python-memcached, kazoo) で置き換える必要がある。

Pinterest: geventでコードベースをマルチスレッドに - ワザノバ | wazanova

ちなみにPostgreSQLだとpsycopg2はgeventモンキーパッチができるらしい。

サードパーティのライブラリがC拡張であると割り込めないのでどうしようもないのです。

サードパーティのライブラリで一番使っているのはそうです、DBドライバです。

肝心のDBドライバがブロックしちゃうともったいないわけです。

これって問題だなーと思ったpsycopg2の人たちは拡張できる仕組みを入れました。

(2.2以降)

greenletを使う場合にはPostgreSQLを使う方がいいという話 - def __mopemope__(self, *args, **kwargs):

msyktmsykt 2013/11/22 23:48 > 実装レベルの話でいうと、JavaのWebアプリは大半がServletベースのものだと思うのでHTTPリクエストはスレッドに対応する。スレッド単位にコネクションを割り当てて使う感じ。なのでThreadLocal使ってコネクションプールは実装されていると思う。

DBCPやAPサーバーに付属しているコネクションプールからコネクションを取り出しても、ServletのリクエストスレッドのThreadLocalへのバインドは自動的には行わないんじゃないですかね。もちろんプールから取り出してバインドするコードを書けばできますが。

ServletのリクエストスレッドにDBコネクションをバインドすると、HTTP同時接続数=DBのコネクションプールサイズとなり、1HTTPリクエストに対してDBコネクションの占有時間が長くなってしまうので、プールサイズが大きくできない場合はHTTP接続数が頭打ちになってしまいます。

wyukawawyukawa 2013/11/23 12:54 コメントありがとうございます。

ThreadLocal云々の部分はちょっと自分でも分かってなかったので無視してください。
かなり前にSlim3のコネクションプーリングの実装を読んでThreadLocalを使ってた記憶があったのでその印象で書きました。
http://d.hatena.ne.jp/wyukawa/20081129/1227971366

msyktmsykt 2013/11/24 21:38 > http://d.hatena.ne.jp/wyukawa/20081129/1227971366

見ました。Slim3はThreadLocalにDBコネクションをbindしてるんですね。知りませんでした。ありがとうございます!!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/wyukawa/20131116/1384621867