结论:testOnBorrow能够确保我们每次都能获取到可用的连接,但如果设置成true,则每次获取连接的时候都要到数据库验证连接有效性,这在高并发的时候会造成性能下降,可以将testOnBorrow设成false,testWhileIdle设置成true这样能获得比较好的性能。

  • testWhileIdle是什么意思?

testWhileIdle:如果为true(默认true),当应用向连接池申请连接,并且testOnBorrow为false时,连接池将会判断连接是否处于空闲状态,如果是,则验证这条连接是否可用。

  • testWhileIdle什么时候会起作用?

  1. 获取连接时;
  2. testOnBorrow==false;
  3. testWhileIdle==true;

使用代码在DruidDataSource的getConnectionDirect方法
注意:此时判断连接空闲的依据是空闲时间大于timeBetweenEvictionRunsMillis(默认1分钟),并不是使用minEvictableIdleTimeMillis跟maxEvictableIdleTimeMillis也就是说如果连接空闲时间超过一分钟就测试一下连接的有效性,但并不是直接剔除;而如果空闲时间超过了minEvictableIdleTimeMillis则会直接剔除。

if (testOnBorrow) {boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}discardConnection(poolableConnection.holder);continue;}} else {if (poolableConnection.conn.isClosed()) {discardConnection(poolableConnection.holder); // 传入null,避免重复关闭continue;}if (testWhileIdle) {final DruidConnectionHolder holder = poolableConnection.holder;long currentTimeMillis             = System.currentTimeMillis();long lastActiveTimeMillis          = holder.lastActiveTimeMillis;long lastExecTimeMillis            = holder.lastExecTimeMillis;long lastKeepTimeMillis            = holder.lastKeepTimeMillis;if (checkExecuteTime&& lastExecTimeMillis != lastActiveTimeMillis) {lastActiveTimeMillis = lastExecTimeMillis;}if (lastKeepTimeMillis > lastActiveTimeMillis) {lastActiveTimeMillis = lastKeepTimeMillis;}long idleMillis                    = currentTimeMillis - lastActiveTimeMillis;long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;if (timeBetweenEvictionRunsMillis <= 0) {timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;}if (idleMillis >= timeBetweenEvictionRunsMillis|| idleMillis < 0 // unexcepted branch) {boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}discardConnection(poolableConnection.holder);continue;}}}}
  • 总结

testWhileIdle的作用跟testOnBorrow是差不多的,都是在获取连接的时候测试连接的有效性,如果两者都为true,则testOnBorrow优先级高,则不会使用到testWhileIdle。

  • testOnBorrow是什么意思?

testOnBorrow:如果为true(默认false),当应用向连接池申请连接时,连接池会判断这条连接是否是可用的。

  • testOnBorrow什么时候会用到?

这个参数主要在DruidDataSource的getConnectionDirect方法中用到

    public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {int notFullTimeoutRetryCnt = 0;for (;;) {// handle notFullTimeoutRetryDruidPooledConnection poolableConnection;try {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;}//测试即将返回的连接是否可用if (testOnBorrow) {boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}discardConnection(poolableConnection.holder);continue;}}//。。。}
  • 连接池是如何判断连接是否有效的?

  • 如果是常用的数据库,则使用${DBNAME}ValidConnectionChecker进行判断,比如Mysql数据库,使用MySqlValidConnectionChecker的isValidConnection进行判断;
  • 如果是其他数据库,则使用validationQuery判断;
    protected boolean testConnectionInternal(DruidConnectionHolder holder, Connection conn) {String sqlFile = JdbcSqlStat.getContextSqlFile();String sqlName = JdbcSqlStat.getContextSqlName();if (sqlFile != null) {JdbcSqlStat.setContextSqlFile(null);}if (sqlName != null) {JdbcSqlStat.setContextSqlName(null);}try {//如果是常用的数据库,则使用相关数据库的validConnectionChecker进行验证,比如MysqlValidConnectionChecker,否则使用validationQuery验证if (validConnectionChecker != null) {boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);long currentTimeMillis = System.currentTimeMillis();if (holder != null) {holder.lastValidTimeMillis = currentTimeMillis;holder.lastExecTimeMillis = currentTimeMillis;}if (valid && isMySql) { // unexcepted branchlong lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);if (lastPacketReceivedTimeMs > 0) {long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;if (lastPacketReceivedTimeMs > 0 //&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {discardConnection(holder);String errorMsg = "discard long time none received connection. "+ ", jdbcUrl : " + jdbcUrl+ ", jdbcUrl : " + jdbcUrl+ ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;LOG.error(errorMsg);return false;}}}if (valid && onFatalError) {lock.lock();try {if (onFatalError) {onFatalError = false;}} finally {lock.unlock();}}return valid;}if (conn.isClosed()) {return false;}if (null == validationQuery) {return true;}Statement stmt = null;ResultSet rset = null;try {stmt = conn.createStatement();if (getValidationQueryTimeout() > 0) {stmt.setQueryTimeout(validationQueryTimeout);}rset = stmt.executeQuery(validationQuery);if (!rset.next()) {return false;}} finally {JdbcUtils.close(rset);JdbcUtils.close(stmt);}if (onFatalError) {lock.lock();try {if (onFatalError) {onFatalError = false;}} finally {lock.unlock();}}return true;} catch (Throwable ex) {// skipreturn false;} finally {if (sqlFile != null) {JdbcSqlStat.setContextSqlFile(sqlFile);}if (sqlName != null) {JdbcSqlStat.setContextSqlName(sqlName);}}}
    public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {if (conn.isClosed()) {return false;}//如果数据库驱动有pingInternal方法,则使用pingInternal方法判断,否则使用validateQuery进行判断if (usePingMethod) {if (conn instanceof DruidPooledConnection) {conn = ((DruidPooledConnection) conn).getConnection();}if (conn instanceof ConnectionProxy) {conn = ((ConnectionProxy) conn).getRawObject();}if (clazz.isAssignableFrom(conn.getClass())) {if (validationQueryTimeout <= 0) {validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;}try {ping.invoke(conn, true, validationQueryTimeout * 1000);} catch (InvocationTargetException e) {Throwable cause = e.getCause();if (cause instanceof SQLException) {throw (SQLException) cause;}throw e;}return true;}}String query = validateQuery;if (validateQuery == null || validateQuery.isEmpty()) {query = DEFAULT_VALIDATION_QUERY;}Statement stmt = null;ResultSet rs = null;try {stmt = conn.createStatement();if (validationQueryTimeout > 0) {stmt.setQueryTimeout(validationQueryTimeout);}rs = stmt.executeQuery(query);return true;} finally {JdbcUtils.close(rs);JdbcUtils.close(stmt);}}
  • 如果验证不通过怎么办?

验证不通过则会直接关闭该连接,并重新从连接池获取下一条连接;

            //测试即将返回的连接是否可用if (testOnBorrow) {boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}//验证不通过则直接剔除该连接,并重新获取下一条连接discardConnection(poolableConnection.holder);continue;}}
  • 总结

testOnBorrow能够确保我们每次都能获取到可用的连接,但如果设置成true,则每次获取连接的时候都要到数据库验证连接有效性,这在高并发的时候会造成性能下降,可以将testOnBorrow设成false,testWhileIdle设置成true这样能获得比较好的性能。

作者:codeimport
链接:https://www.jianshu.com/p/edb6a91285be
来源:简书

testWhileIdle和testOnBorrow相关推荐

  1. 【连接池】Tomcat 连接池中 maxActive,maxWait,maxAge,testOnBorrow,testWhileIdle等选项的作用

    前言 连接池本质作用是为客户端提供连接复用,提升连接效率,降低系统开销.Tomcat的连接池提供了maxActive,maxWait,maxIdle,minIdle,initialSize等参数,配置 ...

  2. com.alibaba.druid.pool.DruidDataSource  : testWhileIdle is true, validationQuery not set

    2019-10-13 16:37:56.965 ERROR 8088 --- [on(6)-127.0.0.1] com.alibaba.druid.pool.DruidDataSource   : ...

  3. 严重: maxIdle is deprecated,严重: testWhileIdle is true, validationQuery not set,Druid连接池连接MSQL报错处理

    JDK9  引发的血案 1.因为使用mysql-connector的依赖版本对应的mysql数据库冲突,mysql8需要使用8.0.11以上的高版本2.jdk9的反射本身存在BUG,会有warning ...

  4. 清瘦的记录者: 一个比dbutils更小巧、好用的的持久化工具

    https://gitee.com/bitprince/memory 1. 概述 1.1 连接.语句和结果集 从JDBC的规范上看,其对数据访问层有相当简洁的抽象:1.连接(connection) 2 ...

  5. idea ssm框架 mysql_idea搭建简单ssm框架的最详细教程(新)

    为开发一个测试程序,特搭建一个简单的ssm框架,因为网上看到很多都是比较老旧的教程,很多包都不能用了,eclipes搭建并且其中还附带了很多的其他东西,所以特此记录一下mac中idea搭建过程. 另: ...

  6. springboot 集成jpa_基于Spring Boot+JPA Restful 风格的数据

    第一章 Restful简介 Restful是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服 务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次, ...

  7. db2 springboot 整合_springboot的yml配置文件通过db2的方式整合mysql的教程

    springboot整合MySQL很简单,多数据源就master,slave就行了,但是在整合DB2就需要另起一行,以下是同一个yml文件 先配置MySQL,代码如下 spring: datasour ...

  8. mysql duplicate jpa_SpringBoot Jpa 双数据源mysql + oracle + liquibase+参考源码

    spring: # 数据库配置 datasource: primary: jdbc-url: jdbc:mysql://localhost:3306/mes-dev?useUnicode=true&a ...

  9. springboot 引入jdbc驱动_SpringBoot整合jdbc、durid、mybatis详解,数据库的连接就是这么简单...

    SpringBoot底层统一采用SpringData处理数据库,这一章主要来讲一下SpringBoot整合jdbc.durid.mybatis的方式. (一)整合jdbc 整合jdbc主要有三步: 1 ...

最新文章

  1. Flask框架(SQLAlchemy(python3版本)中修改数据的方法和删除数据 的方法)
  2. Git之删除本地无用分支
  3. php为什么在变量前加,php中变量前加、@等符号是什么意思?
  4. 更改配置:远程访问gitlab的postgresql数据库
  5. sox处理mp3_音频处理常用Linux命令总结(一)
  6. Job for smbd.service failed because the control process exited with error code. See “systemctl statu
  7. 博客园客户端UAP开发随笔 -- 适配不同尺寸的屏幕
  8. C# mysql 链接 遇到 异常 Authentication with old password no longer supported, use 4.1 style passwords....
  9. 华为harmonyos公测,华为开启HarmonyOS2.0开发者Beta公测招募第二期
  10. 怎么修改chrome浏览器的字体
  11. Flask部署机器学习模型---基于线性回归模型的销售预测系统实现简易版代码
  12. client wants service A, but it has B. Dropping connection.
  13. css cubic-bezier,CSS3 cubic-bezier 函数功能演示
  14. matlab(1):画图像修改曲线形状
  15. android 记录美剧观看进度,[推荐]i看美剧应用:美剧播出、新闻发生提醒直接推送到手机...
  16. 06 聚类算法 - 代码案例二 - K-Means算法和Mini Batch K-Means算法比较
  17. rust旋转摆放_Rust | 种植房教程(新版) - Tamura77
  18. 案例分享:Qt+RV1126+PLC医疗血浆采集仪(中英文输入、西门子PLC、数据库存储,各种数据统计,数值监测,电子秤操作,记录查询,全局报警等等)
  19. Windows批处理bat常用命令教程
  20. Vue.js is detected on this page. Devtools inspection is not available becaus...的解决方法

热门文章

  1. 关于TLP521-1的光耦的导通的试验报告
  2. reverse1题解
  3. 王立铭教授生命科学50讲-细胞的数学原理之一,生命的神奇之处
  4. Linux dd与cp区别
  5. java 重试_Java实现几种简单的重试机制
  6. 什么是IP地址?连接无线网提示“无IP地址分配”该怎么解决?
  7. RK3399开机LOGO替换及ADB的使用技巧
  8. 「论文自译」MIT 6.824 In Search of an Understandable Consensus Algorithm (Extended Version)
  9. Understand:高效代码静态分析神器详解(一)
  10. burp的配置及抓包百度的过程