概要
SpringDataJPAを使用していた際に疑問となった、DB接続周りについて調査した。
環境
Java 8
SpringBoot 1.5.7.RELEASE
コネクションの調査
DBに接続するアプリケーションを起動すると、コネクションが10本自動で張られていた。
アプリケーションを立ち上げる際にDBが落ちていると起動が失敗するのでなるほどなあという感じ。
(一番上のコネクションは確認用に接続しているもの)
コネクションが10本自動で張られるのは、使用されているDBコネクションプーリングの初期値initialSizeによるのではと思います。
tomokaneさんよりコメントいただきました。
今回のケースだと、spring.datasource.tomcat.initial-sizeの値を変えると起動時のコネクション数が変わりました。
mysql> show processlist;
+-----+------+------------------+---------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+------------------+---------+---------+------+----------+------------------+
| 3 | user | localhost | test_db | Query | 0 | starting | show processlist |
| 108 | user | 172.19.0.1:45338 | test_db | Sleep | 22 | | NULL |
| 109 | user | 172.19.0.1:45340 | test_db | Sleep | 22 | | NULL |
| 110 | user | 172.19.0.1:45342 | test_db | Sleep | 22 | | NULL |
| 111 | user | 172.19.0.1:45344 | test_db | Sleep | 22 | | NULL |
| 112 | user | 172.19.0.1:45346 | test_db | Sleep | 21 | | NULL |
| 113 | user | 172.19.0.1:45348 | test_db | Sleep | 21 | | NULL |
| 114 | user | 172.19.0.1:45350 | test_db | Sleep | 21 | | NULL |
| 115 | user | 172.19.0.1:45352 | test_db | Sleep | 21 | | NULL |
| 116 | user | 172.19.0.1:45354 | test_db | Sleep | 21 | | NULL |
| 117 | user | 172.19.0.1:45356 | test_db | Sleep | 21 | | NULL |
+-----+--------+------------------+----------------+---------+------+----------+------------------+
また同時に10を超えるアクセスを立ち上げたアプリケーション経由でしたところ、コネクションが増えたり全て使われている様子はなかった。
どのようにコントロールされているかまでは未調査。
トランザクションの調査
トランザクションをするためには、トランザクションの範囲にしたいメソッドに対して「@Transactional」アノテーションを付与する。
@Transactional
public void methodName() {
// SELECT文その1
doSelect1();
// 10秒スリープ
try{
Thread.sleep(10000);
} catch(Exception e) {
}
// SELECT文その2
doSelect2();
}
確認のため、上記のようなメソッドを作成した。
スリープの間にSELECT文その2の対象となるレコードを挿入してもSELECT結果には含まれていなかった。
タイムアウトについて
application.propertiesにて値を設定できる。
// 20秒
spring.transaction.default-timeout=20
個別の設定はアノテーションで行う。
// 10秒
@Transactional(timeout = 10)
DB側でもタイムアウトが設定しており、MySQLだと以下のようになっていた。
innodb_lock_wait_timeoutがその値で、初期値が50秒となる。
mysql> show global variables like '%wait%';
+---------------------------------------------------+----------+
| Variable_name | Value |
+---------------------------------------------------+----------+
| innodb_lock_wait_timeout | 50 |
| innodb_spin_wait_delay | 6 |
| lock_wait_timeout | 31536000 |
| performance_schema_events_waits_history_long_size | 10000 |
| performance_schema_events_waits_history_size | 10 |
| wait_timeout | 28800 |
+---------------------------------------------------+----------+
挙動を確認するため、行ロックをかけたレコードに対してJpaRepositoryのsaveメソッドを実行した。
行ロックは下記の用にあらかじめmysqlサーバに入って行っておく。
mysql> start transaction; Query OK, 0 rows affected (0.01 sec)
mysql> update test_db column1 = 'hoge' WHERE column2 = '0'; Query OK, 0 rows affected (0.00 sec)
Rows matched: 2 Changed: 0 Warnings: 0
この行ロックに引っかかるようsaveメソッドを実行したところ、設定値の時間後に上記exceptionが投げられるのを確認した。
(ちなみにログを見る限り、saveメソッドの内部ではSELECT→UPDATEが行われている様子)
org.springframework.dao.CannotAcquireLockException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not execute statement