Druid源码分析_06_testOnBorrow、testOnReturn、testWhileIdle属性

属性定义

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)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值