Open4

【記事の草案】DBのコネクションプールについてちゃんと理解する

おっちー(O.S)おっちー(O.S)

前提知識

アプリのコードからデータベースにアクセスする際、通常、次のような手順を踏む。

  1. アプリとデータベースとの接続を確立する
  2. トランザクションを開始する
  3. SQLを実行する
  4. トランザクションを終了する(コミットまたはロールバック)
  5. データベースとの接続を切断する

コネクションプーリングとは?

コネクションプールとは、データベースへの接続を確立した状態で保持しておき、そのコネクションを再利用することでデータベース接続のオーバーヘッドを削減する機能のことである。
先ほどの処理において、1.5.によるオーバーヘッドが大きいため、コネクションプールを使用して接続を保持し、これを再利用する。

コネクションプールの数はどのように決めたら良いのか?

コネクションをプールしておくということは、それだけ並列に処理を実行できるということである。
したがって、コネクションプールの数を決める際には、以下の要因を考慮すると良い。

  • DBサーバーのスレッド数:コネクションプールの数が、DBサーバーが処理できる並列スレッド数を超えないように設定する。一般的には、1台のDBサーバーの場合、DBサーバーのCPUコア数に基づいてコネクションプールの数を設定することが多い。
  • アプリケーションの同時接続数:アプリケーションがどれだけの同時接続を必要とするかも重要である。
おっちー(O.S)おっちー(O.S)

より深く理解するために

railsではどうなってる?

  • config/database.ymlでコネクションプーリングの設定ができる。
production:
  adapter: postgresql
  encoding: unicode
  database: my_database
  pool: 15
  timeout: 3000       # コネクション取得の最大待ち時間を3秒に設定
  reaping_frequency: 30 # アイドル状態になったコネクションプールを破棄する頻度を30秒に設定
  checkout_timeout: 2000  # コネクション取得時のタイムアウトを2秒に設定

実例やDeep Dive

https://qiita.com/hiroshi-ishihara/items/449eb120fece242f1a0d
https://qiita.com/KazuyaTomita/items/55699874d96800e1e69c
https://hackerslab.aktsk.jp/technology/rails4_connection_pooling/
https://engineering.grab.com/deep-dive-into-database-timeouts-in-rails
https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts

おっちー(O.S)おっちー(O.S)

https://engineering.grab.com/deep-dive-into-database-timeouts-in-rails
上記の記事の、コネクションプーリングのアルゴリズムがわかりやすかったので抜粋。

コネクションプーリングのアルゴリズムの説明

  1. 既存のコネクションが利用可能であれば、それをすぐに返す。
  2. プールがキャパシティいっぱいの場合は、キューで待機する。checkout_timeoutを超過したらエラーを発生させる。超過していない場合は、利用可能なコネクションを返す。
  3. プールがキャパシティいっぱいでない場合は、新しいコネクションを作成する。connect_timeoutを超過したらエラーを発生させる。
  4. 新しいコネクションが確立されたら、それを返す。

引用

Connection Pooling Algorithm
The following pseudocode is the algorithm for how ActiveRecord retrieves connections from the pool to perform database queries.

if there are existing connections to the database available:
    return one of the existing connections

if the pool is at capacity:
    wait on the queue, raise exception if `checkout_timeout` has elapsed
    return one of the now available connections

# pool is not at capacity
try to create a new connection, raise exception if `connect_timeout` has elapsed

# connection to database established
return new connection

This is loosely translated from the source code.