testWhileIdle和testOnBorrow

结论: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 notFullTimeoutRetry
            DruidPooledConnection 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 branch
                    long 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) {
            // skip
            return 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
来源:简书

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis 是一种优秀的 ORM 框架,可以方便地对数据库进行操作。而 TDengine 是一种高性能、高可靠性的时序数据库,Druid 是一个开源的数据库连接池,支持并发控制、SQL 监控等功能。下面介绍如何集成 MyBatis、TDengine 和 Druid。 1. 集成 MyBatis 和 TDengine 首先需要在 pom.xml 中添加 TDengine 的依赖: ``` <dependency> <groupId>com.taosdata.jdbc</groupId> <artifactId>taos-jdbc</artifactId> <version>1.7.0</version> </dependency> ``` 然后在 MyBatis 的配置文件中添加 TDengine 数据库的连接信息: ``` <dataSource type="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.taosdata.jdbc.TSDBDriver"/> <property name="url" value="jdbc:TAOS://127.0.0.1:6030/test"/> <property name="username" value="root"/> <property name="password" value="taosdata"/> </dataSource> ``` 其中,“url”中的“127.0.0.1:6030”表示 TDengine 数据库的 IP 地址和端口号,“test”表示要连接的数据库名称,“root”和“taosdata”分别为 TDengine 数据库的用户名和密码。 2. 集成 MyBatis、TDengine 和 Druid 在 pom.xml 中添加 Druid 的依赖: ``` <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> ``` 然后在 MyBatis 的配置文件中添加 Druid 数据库连接池: ``` <dataSource type="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.taosdata.jdbc.TSDBDriver"/> <property name="url" value="jdbc:TAOS://127.0.0.1:6030/test"/> <property name="username" value="root"/> <property name="password" value="taosdata"/> <property name="initialSize" value="5"/> <property name="minIdle" value="5"/> <property name="maxActive" value="20"/> <property name="maxWait" value="60000"/> <property name="timeBetweenEvictionRunsMillis" value="300000"/> <property name="minEvictableIdleTimeMillis" value="600000"/> <property name="testWhileIdle" value="true"/> <property name="validationQuery" value="SELECT 1"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> </dataSource> ``` 其中,“initialSize”表示连接池的初始化大小,“minIdle”表示最小空闲连接数,“maxActive”表示最大活跃连接数,“maxWait”表示最大等待时间,“timeBetweenEvictionRunsMillis”表示两次检查连接池的间隔时间,“minEvictableIdleTimeMillis”表示连接最小空闲时间,“testWhileIdle”表示是否检查空闲连接,“validationQuery”表示连接校验语句,“testOnBorrow”表示是否检查连接的可用性,“testOnReturn”表示连接返回给连接池时是否检查可用性,“poolPreparedStatements”表示是否缓存 PreparedStatement,“maxPoolPreparedStatementPerConnectionSize”表示每个连接最大缓存的 PreparedStatement 数量。 最后在 MyBatis 的配置文件中添加 Druid 的相关配置: ``` <dataSource type="com.alibaba.druid.pool.DruidDataSource"> ... </dataSource> <plugins> <plugin interceptor="com.alibaba.druid.pool.DruidDataSourceStatInterceptor"/> <plugin interceptor="com.alibaba.druid.wall.WallFilter"/> </plugins> ``` 其中,“com.alibaba.druid.pool.DruidDataSourceStatInterceptor”表示 Druid 的连接池监控插件,“com.alibaba.druid.wall.WallFilter”表示 Druid 的 SQL 检查插件。 以上就是 MyBatis 集成 TDengine 和 Druid 的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值