源码地址
druid常用配置
# 创建DB连接4要素
spring.datasource.druid.url=xxx
spring.datasource.druid.username=xxx
spring.datasource.druid.password=xxx
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
# 在数据库连接池首次创建的时候初始化几个连接
spring.datasource.druid.initial-size=5
# 最多可以激活的连接个数
spring.datasource.druid.max-active=10
# 最小的数据库空闲连接个数
spring.datasource.druid.min-idle=5
# 创建连接时的最大等待时长,一般这个不在意
spring.datasource.druid.max-wait=60000
# 当执行数据库连接存活检查时,使用的通信sql语句
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
# 是不是在每次获取连接的时候都进行检查,设置为fale,对性能影响高
spring.datasource.druid.test-on-borrow=false
# 是不是在每次归还(事务执行结束)连接的时候检查,对性能影响高
spring.datasource.druid.test-on-return=false
# 是否在连接空闲的时候sql检查连接(这个参数只有在获取连接的时候才会使用)
spring.datasource.druid.test-while-idle=true
# 在销毁线程中表示的是执行的时间间隔
# 在获取连接的时候,配合test-while-idle=true使用
# 表示的最小的空闲时间,大于这个时间才会进行sql检查连接
spring.datasource.druid.time-between-eviction-runs-millis=60000
# 最小的连接空闲时间,小于这个时间不会进行回收
spring.datasource.druid.min-evictable-idle-time-millis=3600000
# 是否开启会话保持,开启了之后如果在销毁的时候低于minidle的数量才会补充
spring.datasource.druid.keep-alive=true
# 开启了会话保持之后,对于不回收的连接
# 多长时间进行validateQuery一次校验,校验失败的话,还需要关闭异常链接并新建
spring.datasource.druid.keep-alive-between-time-millis=3600000
mysql进行jdbc连接
import java.sql.*;
public class JdbcDemo {
public static void main(String[] args) {
try {
// 加载MySQL驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 建立与MySQL的连接
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
// 创建Statement对象
Statement stmt = conn.createStatement();
// 执行查询操作
String sql = "SELECT * FROM users WHERE age > 18";
ResultSet rs = stmt.executeQuery(sql);
// 处理查询结果
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println("id=" + id + ", name=" + name + ", age=" + age);
}
// 关闭资源
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException
e) {
e.printStackTrace();
}
}
}
druid各配置项生效原理
初始化过程
com.alibaba.druid.pool.DruidDataSource#init
this.connections = new DruidConnectionHolder[this.maxActive];
this.evictConnections = new DruidConnectionHolder[this.maxActive];
this.keepAliveConnections = new DruidConnectionHolder[this.maxActive];
while(this.poolingCount < this.initialSize) {
try {
DruidAbstractDataSource.PhysicalConnectionInfo pyConnectInfo = this.createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
this.connections[this.poolingCount++] = holder;
} catch (SQLException var18) {
LOG.error("init datasource error, url: " + this.getUrl(), var18);
if (this.initExceptionThrow) {
connectError = var18;
break;
}
Thread.sleep(3000L);
}
}
上面是连接池的初始化过程,数组长度是maxActive, 代表最多能拥有的连接个数,同时分为三个数组存放。
然后循环初始化数据库连接,保存至数据中,初始化的个数是initialSize个
线程任务
连接创建线程
通过信号量等控制线程运行,从而保持线程池数量。这个先不管
com.alibaba.druid.pool.DruidDataSource.CreateConnectionThread#run
连接销毁线程
com.alibaba.druid.pool.DruidDataSource.DestroyConnectionThread#run
while(true) {
try {
if (DruidDataSource.this.closed || DruidDataSource.this.closing) {
break;
}
if (DruidDataSource.this.timeBetweenEvictionRunsMillis > 0L) {
Thread.sleep(DruidDataSource.this.timeBetweenEvictionRunsMillis);
} else {
Thread.sleep(1000L);
}
if (Thread.interrupted()) {
break;
}
DruidDataSource.this.destroyTask.run();
} catch (InterruptedException var2) {
break;
}
}
timeBetweenEvictionRunsMillis:这个参数是标识这个线程需要sleep多久之后才能唤醒下一次销毁执行
com.alibaba.druid.pool.DruidDataSource.DestroyTask#run
// 连接池的空闲数量 去除 最小保留的空闲连接
checkCount = this.poolingCount - this.minIdle;
// 记录空闲时长
idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;
// 如果空闲时长 大于最小空闲时间 并且在保留的最小空闲连接之外则移除
if (idleMillis >= this.minEvictableIdleTimeMillis) {
if (checkTime && i < checkCount) {
this.evictConnections[evictCount++] = connection;
continue;
}
// 如果空闲时间比最大的空闲时间还大,则直接移除
if (idleMillis > this.maxEvictableIdleTimeMillis) {
this.evictConnections[evictCount++] = connection;
continue;
}
}
minEvictableIdleTimeMillis:空闲连接的最小保留时长,低于这个时间不会回收。高于这个时间,则移除比minIdle多出数量的连接。
maxEvictableIdleTimeMillis:默认7个小时,不管个数,空闲时间达到即移除。
通过keepalive参数进行保活
// 这个是不满足驱逐逻辑之后才执行的
// 如果设置了保持存活,且连接的空闲时间大于应该保持存活的时间了
if (keepAlive && idleMillis >= this.keepAliveBetweenTimeMillis) {
this.keepAliveConnections[keepAliveCount++] = connection;
}
// 需要保活,且数量低于最小空闲连接,则需要补充,就是通过唤起创建线程补充
if (keepAlive && this.poolingCount + this.activeCount < this.minIdle) {
needFill = true;
}
keepAlive:是否需要保持连接存活
keepAliveBetweenTimeMillis:多久进行一次保活。
keepAliveConnections:有需要保活的连接,则利用validationQuery进行保活。保活失败,则重新创建连接,依赖于上述两个配置。
needFill:如果需要补充连接的话,也会重新创建连接。依赖于keepAlive以及连接数量情况。
获取连接过程
com.alibaba.druid.pool.DruidDataSource#getConnectionDirect
// testOnBorrow 参数的使用
if (this.testOnBorrow) {
// 如果
boolean validate = this.testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (validate) {
break;
}
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
this.discardConnection(poolableConnection.holder);
}
// testWhileIdle参数的使用
if (!this.testWhileIdle) {
break;
}
long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
// 只有空闲时间大于timeBetweenEvictionRunsMillis 这个时间,才会进行验证
if (idleMillis < timeBetweenEvictionRunsMillis && idleMillis >= 0L) {
break;
}
// 验证失败就会丢弃连接
boolean validate = this.testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (validate) {
break;
}
this.discardConnection(poolableConnection.holder);
testOnBorrow:获取连接的时候进行检测
testWhileIdle:获取连接的时候,且其空闲时间大于timeBetweenEvictionRunsMillis开始进行检测