1. Druidの使用
Druidは、Alibabaが開発した高性能なデータソース接続プールです。
1.1. Spring BootプロジェクトへのDruid統合
1.1.1. Maven依存関係の設定
<!-- Druid依存関係 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.15</version>
</dependency>
1.1.2. データソースの関連設定
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDataTimeBehavior=convertToNull&useSSL=false
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
maxInactive: 10
minIdle: 5
timeBetweenEvictionRunsMillis: 5000
minEvictableIdleTimeMillis: 10000
filters: stat,wall
testOnBorrow: false
1.1.3. DruidConfig設定ファイルの定義
package com.lucky.test.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource createDataSource() {
return new DruidDataSource();
}
}
DruidDataSourceの接続プールを初期化し、アプリケーションが使用するデータソースとして設定します。
| 設定項目 | 例 | 説明 |
|---|---|---|
| url |
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDataTimeBehavior=convertToNull&useSSL=false |
データベースへの接続URL |
| username | root | データベース接続のユーザー名 |
| password | root | データベース接続のパスワード |
| initialSize | 5 | 接続プールの初期接続数 |
| minIdle | 5 | 接続プールの最低空閑接続数 |
| maxActive | 20 | 接続プールの最大アクティブ接続数 |
| maxWait | 60000 | 接続取得のタイムアウト待ち時間(ミリ秒) |
| timeBetweenEvictionRunsMillis | 5000 | 接続のIdleチェック間隔(ミリ秒) |
| minEvictableIdleTimeMillis | 10000 | Idle接続を廃棄するための最小Idle時間(ミリ秒) |
2. Druid接続プールのソース解析
2.1. 接続プールの初期化
DruidDataSourceの初期化方法は以下です。
public void init() throws SQLException {
if (initialized.get()) {
return;
}
ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new SQLException("interrupted", e);
}
try {
// 接続IDの生成
this.id = DruidDriver.createDataSourceId();
// 初期接続の作成
int initialSize = getInitialSize();
for (int i = 0; i < initialSize; i++) {
Connection conn = createConnection();
ConnectionHolder holder = new ConnectionHolder(this, conn);
connections[poolingCount] = holder;
poolingCount++;
}
// 接続管理スレッドの起動
startConnectionCreateThread();
startConnectionDestroyThread();
initializationLatch.await();
} finally {
initialized.set(true);
lock.unlock();
}
}
初期化の主な手順は以下のとおりです。
- 初期化済みかどうかをチェック
- ロックをかけて並列初期化を防止
- 初期接続数を設定
- 接続管理スレッドを起動
2.2. 接続の取得
public PooledConnection getConnection(long maxWaitMillis) throws SQLException {
if (disabled.get()) {
throw new SQLException("データソースが無効です");
}
ConnectionHolder holder = null;
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new SQLException("interrupted", e);
}
try {
// 接続プールの空き接続をチェック
if (poolingCount == 0) {
// 空き接続がない場合、接続作成を待機
if (maxWaitMillis <= 0) {
throw new SQLException("接続プールがいっぱいです");
}
empty.await(maxWaitMillis, TimeUnit.MILLISECONDS);
}
// 空き接続の取得
holder = connections[--poolingCount];
connections[poolingCount] = null;
// 接続の有効性チェック
if (testOnBorrow.get()) {
if (!testConnection(holder.getConnection())) {
return null;
}
}
// 接続の貸し出し
PooledConnection pooledConn = new PooledConnection(holder);
activeCount++;
return pooledConn;
} finally {
lock.unlock();
}
}
2.3. 接続の返却
public void returnConnection(PooledConnection pooledConn) throws SQLException {
ConnectionHolder holder = pooledConn.getHolder();
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new SQLException("interrupted", e);
}
try {
// 接続の返却処理
if (holder.isDiscarded()) {
return;
}
// 接続プールへの返却
if (poolingCount < maxActive.get()) {
connections[poolingCount++] = holder;
notEmpty.signal();
} else {
// 最大接続数に達している場合は接続を廃棄
discardConnection(holder.getConnection());
}
} finally {
lock.unlock();
}
}
3. Druidの実装詳細
3.1. 核心クラス
- DruidDataSource: データソースの実装クラス、getConnection()メソッドを提供
- PooledConnection: データベース接続を表すクラス
- ConnectionHolder: 接続プール内で管理される接続のラッパー
3.2. ReentrantLockとConditionの使用
Druidは、ReentrantLockとConditionを用いて接続の取得と返却を同期管理しています。
- empty: 接続プールが空の場合、接続作成スレッドを通知するためのCondition
- notEmpty: 接続プールに空き接続がある場合、接続取得スレッドを通知するためのCondition
3.3. Druidの動作フロー
getConnection()メソッドの動作フロー:
- ロックを取得
- 接続プールの状態をチェック
- 接続を取得
- ロックを解放
接続作成スレッドの動作フロー:
- ロックを取得
- 接続プールの状態をチェック
- 接続を作成
- ロックを解放