属性定义
mysql默认使用ping模式,可以通过设置系统属性System.getProperties().setProperty(“druid.mysql.usePingMethod”, “false”)更改为sql模式
用来检测连接是否有效的sql,要求是一个查询语句,常用select ‘x’。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。默认:SELECT 1
spring.emily.datasource.config.mysql.validation-query: "SELECT 1"
单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法,默认:-1
spring.emily.datasource.config.mysql.validation-query-timeout: -1
#申请连接时执行validationQuery检测连接是否有效,这个配置会降低性能。默认:false(如果test-on-borrow为true,那么test-while-idle无效)
spring.emily.datasource.config.mysql.test-on-borrow: false
#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。默认:true
spring.emily.datasource.config.mysql.test-while-idle: true
#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认:false
spring.emily.datasource.config.mysql.test-on-return: false
1、testOnBorrow
入口
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException
作用
从数据库链接池中获取后,进行校验,避免失效的链接被使用,造成错误
源码
// handle notFullTimeoutRetry
DruidPooledConnection poolableConnection;
try {
// 核心逻辑
System.out.println("getConnectionInternal");
// 获取的池化数据库链接
poolableConnection = getConnectionInternal(maxWaitMillis);
} catch (GetConnectionTimeoutException ex) {
if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
notFullTimeoutRetryCnt++;
if (LOG.isWarnEnabled()) {
LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);
}
continue;
}
throw ex;
}
// testOnBorrow =false 每次获取链接都会验证
if (testOnBorrow) {
// 发送一条validateSql SELECT 1
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
// 如果不可用就回收
discardConnection(poolableConnection.holder);
continue;
}
}
分析
申请链接时候会进行一次验证,比较消耗性能,我的理解是无论是开发环境还是生产环境都不需要打开,Druid 1.2.8版本已经有其他的属性去在获取链接时判断链接的有效性
2、testOnReturn
入口方法
当连接使用完,调用commit或者rollback方法后,连接池会回收该连接
// 回收链接时候
protected void recycle(DruidPooledConnection pooledConnection)
作用
确保归还的时候链接是有效的,如果无效则会丢弃,丢弃的过程中会用lock锁,造成其他线程的堵塞,
源码
if (testOnReturn) {
boolean validate = testConnectionInternal(holder, physicalConnection);
if (!validate) {
JdbcUtils.close(physicalConnection);
destroyCountUpdater.incrementAndGet(this);
lock.lock();
try {
if (holder.active) {
activeCount--;
holder.active = false;
}
closeCount++;
} finally {
lock.unlock();
}
return;
}
}
分析
每次事务提交或者回滚都会回收链接到连接池中,回收过程对链接判断是否有效,进行验证,浪费性能.
3、testWhileIdle
入口
与testOnBorrow一致,个人认为testWhileIdle就是代替testOnBorrow的
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException
作用
如果每次从数据库连接池获取链接都进行验证,性能太差,所以引入了空闲时间,当某个链接获取时,快速的check其是否过期.当判断是过期的链接,则进行校验.
源码
if (testWhileIdle) {
final DruidConnectionHolder holder = poolableConnection.holder;
long currentTimeMillis = System.currentTimeMillis();
// // 记录该链接最后活动时间
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
// 记录该链接最后执行时间
long lastExecTimeMillis = holder.lastExecTimeMillis;
// 记录最后心跳时间
long lastKeepTimeMillis = holder.lastKeepTimeMillis;
// 是否检查执行时间 checkExecuteTime 默认false
if (checkExecuteTime
&& lastExecTimeMillis != lastActiveTimeMillis) {
lastActiveTimeMillis = lastExecTimeMillis;
}
// 如果最后一次心跳时间 大于 最后活动的时间,则赋值
if (lastKeepTimeMillis > lastActiveTimeMillis) {
lastActiveTimeMillis = lastKeepTimeMillis;
}
// 空闲时间 = 当前时间减去 - 最后活跃的时间
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
// 默认60秒
long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
if (timeBetweenEvictionRunsMillis <= 0) {
timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
}
// 如果空闲时间 大于等于 60秒
if (idleMillis >= timeBetweenEvictionRunsMillis || idleMillis < 0 ) {
// 发送测试语句 SELECT 1 确保回收的是有效的链接
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
// 回收
discardConnection(poolableConnection.holder);
continue;
}
}
}
分析
如果每次从数据库连接池获取链接都进行验证,性能太差,这就是原来testOnBorrow的做法.不可取.
所以引入了testWhileIdle,吸取了testOnBorrow的弊端,设计了保活机制,增加了校验的前置判断,对从数据库连接池中获取的链接先进行保活判断,不满足主观的保活判断,再客观判断(就是SELECT 1)