記事制作経緯
Springbootでアプリを作成するうえで必須と言っていいほど使われているHikariCPですが、きちんと理解はできていませんでした。
でも、HikariCPに関するエラーって結構見かけるんですよね。
そのたびにエラーの原因について調べてなあなあに対処していたのですが、
そろそろちゃんと理解したほうが良いなと思い、調べて書き残すことにしました。
本記事の目的
HikariCPってなんかDB関連のやつだよね~ぐらいの理解度の人が、
きちんとHikariCPについて理解できるようにすること。
- コネクションプールについて理解する
- HikariCPについて理解する
- HikariCPの設定項目について理解する
JDBCコネクションプールについて
JDBCコネクションプールとは、Java(JDBC)で使用するコネクションプールのことです。
HikariCPはそのうちのひとつになります。
JDBCとは
Java Database Connectivity の略称でJDBCです。
Javaとデータベースを接続するためのJava標準APIです。
コネクションプールとは
Javaがデータベースとやり取りをする流れは次の通りです。
①データベースに繋ぐ(接続を開始)
②SQLを実行する
③データベースとの接続を切断する
このなかで、「①データベースに繋ぐ」というのは負荷が高いです。
接続するには、DBセッションの生成や認証処理など初期処理が必要だからです。
そんなことを毎回行っていたら、負荷もかかりパフォーマンスが落ちることは容易く想像ができますね。
そこで誕生したのがコネクションプールです。
コネクションプールの仕組み
コネクションプールは、
Javaアプリ起動時にあらかじめ一定数のDB接続を作成し、プール(pool:貯める、蓄える)しておく
という仕組みになっています。
Javaアプリは、DB使用時にプールされているコネクションを使用し、
使い終わったらコネクションをプールに返却します。
コネクションプールの役割
コネクションプールの基本的な役割は以下の通りです。
-
高速なDB接続
→あらかじめコネクションを用意しておくことで、レスポンスが速くなる -
同時接続数を限定する
→DBへの過剰負荷が抑えられる
アプリでDBに接続するなら必須アイテムということですね。
コネクションの状態
HikariCPにおけるコネクションは以下の3つの状態で管理されています。
- active :使用中のコネクション
- idle :空いているコネクション
- waiting:空きを待っているスレッド(アプリが接続したがっているが待ち状態)
ログでは以下のように出力されます。
// DBを使用していないとき
HikariPool-1 - Pool stats (total=10, active=0, idle=10, waiting=0)
これはアイドル(待機)状態のコネクションが10個あり、
アクティブ(使用中)状態のコネクションと、コネクション使用待ちのスレッドは存在しないということを表しています。
DBを使用していない間は上記の状態が基本となりますが、
DBを使用する処理が走った際にはactiveにも数字が入ります。
// DBを使用しているとき
HikariPool-1 - Pool stats (total=10, active=3, idle=7, waiting=0)
totalのすべてがactiveのまま、waitingの数字が増え続けている場合などは、
コネクションが返却されていない = コネクションリークが起きていると判断でき、
そのログが出ているあたりの処理を見直すことができます。
// アイドル状態のコネクションがないため、待ちスレッドがある = 異常
HikariPool-1 - Pool stats (total=10, active=10, idle=0, waiting=5)
コネクションの状態のログ出力方法
上記ログはデフォルトではなく、以下を設定ファイルに書き込むことで出力されます。
logging.level.com.zaxxer.hikari: debug
ただ、このログは30秒ごとに出続けるため通常時は出力しないほうがよいです。(ノイズになるので)
タイムアウトやコネクションリークのエラーが起きた際に出力すると、タイミングをつかむことができるので良いと思います。
HikariCPとは
公式の説明を見てみましょう。
Fast, simple, reliable. HikariCP is a "zero-overhead" production ready JDBC connection pool.
翻訳すると、「早くてシンプルで信頼性のあるJDBCコネクションプールですよ」ということですね。
サードパーティライブラリですが、Springbootではデフォルトの接続プールとして使用されています。
SpringBootとHikariCP
以下のスターターを使用している場合、基本はHikariCPが利用されます。
・spring-boot-starter-jdbc
・spring-boot-starter-data-jpa
設定は基本、application.propertiesやapplication.ymlで行います。
HikariCPの設定(最低限)
じゃあ設定はどのようにすればよいのでしょうか?
公式では以下の3つを最低限設定すべき項目として挙げています。
# 接続先DBの場所と名前
spring.datasource.url=jdbc:mysql://localhost:3306/sample
# DBのログインユーザー
spring.datasource.username=dbuser
# DBのパスワード
spring.datasource.password=secret
これはDB接続に必要な情報で、自分が使用しているDBの設定を記入します。
逆に言えば、上記の設定以外はデフォルト値が用意されているので、
極論何も書かなくても、HikariCPは使用できます。
HikariCPの設定(重要項目)
先ほど挙げたものは最低限必須で記載する必要のある設定項目ですが、
一般的に自分で設定することが多い項目もいくつかあります。
そのなかで独断と偏見で最低限この設定については知っておいたほうがいいというものを3つ挙げます。
記載している値は公式のデフォルト値です。
ほかにもあるんだったら知りたいよ~という方は公式サイトを見てみてください。
# コネクション取得のタイムアウト(ミリ秒)
spring.datasource.hikari.connection-timeout=30000
# コネクションが接続し続けられる時間
spring.datasource.hikari.max-lifetime=1800000
# 最大コネクション数
spring.datasource.hikari.maximum-pool-size=10
設定項目について1行でまとめてみたのですが、あまりイメージが湧きにくいかと思います。
これらはコネクションの状態にどう影響するかを理解すると、どのように設定すればよいかがイメージしやすくなります。
①spring.datasource.hikari.connection-timeout
スレッドがコネクションを使用するための待ち時間です。
例)
コネクション数が10個しかないのに接続したいスレッドは13個あるとします。
その場合、10個のスレッドは接続することができますが、3個のスレッドは接続するために待機する必要があります。
その待機時間を最大どれくらい待てるか、というのを決めるのがこの値です。
デフォルトでは30秒となっています。
上記の例でいうと、10個のスレッドがプールに返却せず3個のスレッドが30秒以上待機した場合、コネクションタイムアウトのエラーが起きます。
Connection is not available, request timed out
原因としては「コネクションリーク」「SQLの実行時間が長い」などが考えられます。
②spring.datasource.hikari.max-lifetime
1つのコネクションがDBに接続できる最大時間です。最大時間を超えたコネクションは破棄されます。
使用されている接続には適用されず、閉じられた接続のみです。
この値は設定することを公式によって強く勧められています。
コネクションの接続時間は、DB側でも設定されています。
つまり、DB側からコネクションを切ることがあるということです。
そうなると、アプリが接続しようとしてもDBに接続を切られてしまっているためエラーが起きます。
それを防ぐために自発的にコネクションを破棄する時間を設定するというのがこの値です。
DBの設定よりも小さめに設定することが基本となっています。
ちなみに、それぞれのコネクションは少しずつズレて生成されているため、同時に多くのコネクションが破棄されるということはありません。
③spring.datasource.hikari.maximum-pool-size
最大コネクション数、つまりマックスでDBに接続できる数です。
例えば、この値が10に設定されている場合、15スレッドきてもDBに接続できるのは10スレッドのみということです。
この数が少なすぎると、waitingの数が増えコネクションタイムアウトに繋がります。
ただ、むやみやたらに増やせば解決するというわけでもないので、コネクションタイムアウトの原因が何かというのをきちんと検討する必要があります。
おわりに
Java本体ではないものの、エラーとしてはよく見かけるものなので、
きちんと理解できてよかったです。
私と同じような人の役に立てればと思います。
(特に、公式サイトを見た時の「こんなに設定項目あったんだ...」はおもしろかったので興味のある方はご覧ください。)
参考記事