侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

validationQuery在Druid中的配置

孔子说JAVA
2022-09-08 / 0 评论 / 0 点赞 / 34 阅读 / 5,362 字 / 正在检测是否收录...

Druid是一个由阿里开源的数据库连接池,Druid的配置非常丰富,但是设置不当会对生产环境造成严重影响,validationQuery是Druid用来测试连接是否可用的SQL语句,每种数据库的默认值都不相同,这个查询语句必须是至少返回一条结果的SELECT语句。

1、常见数据库validationQuery

validationQuery是Druid用来验证数据库连接的查询语句,这个查询语句必须是至少返回一条结果的SELECT语句。每种数据库都有各自的验证语句,下表列出了几种常见数据库的validationQuery。

数据库 validationQuery
Oracle select 1 from dual
MySQL select 1
Microsoft SQL Server select 1
DB2 select 1 from sysibm.sysdummy1
SQLite select 1
HSQLDB select 1 from INFORMATION_SCHEMA.SYSTEM_USERS
postgresql select version()
ingres select 1
Apache Derby select 1
H2 select 1
Informix select count(*) from systables

2、何时起作用

当Druid遇到testWhileIdle,testOnBorrow,testOnReturn时,就会验证连接的有效性,验证规则如下:

  • 如果有相关数据库的ValidConnectionChecker,则使ValidConnectionChecker验证(Druid提供常用数据库的ValidConnectionChecker,包括MSSQLValidConnectionChecker,MySqlValidConnectionChecker,OracleValidConnectionChecker,PGValidConnectionChecker);
  • 如果没有ValidConnectionChecker,则直接使用validationQuery验证;

3、如何验证

ValidConnectionChecker是如何验证的?

  • MySqlValidConnectionChecker会使用Mysql独有的ping方式进行验证,其他数据库其实也都是使用validationQuery进行验证。

3.1 MySqlValidConnectionChecker验证方式

    public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
        if (conn.isClosed()) {
            return false;
        }
        //Mysql使用com.mysql.jdbc.MySQLConnection的pingInternal方法进行验证
        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);
        }

    }

3.2 OracleValidConnectionChecker的验证方式

    public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
        if (validateQuery == null || validateQuery.isEmpty()) {
            validateQuery = this.defaultValidateQuery;
        }

        if (conn.isClosed()) {
            return false;
        }

        if (conn instanceof DruidPooledConnection) {
            conn = ((DruidPooledConnection) conn).getConnection();
        }

        if (conn instanceof ConnectionProxy) {
            conn = ((ConnectionProxy) conn).getRawObject();
        }

        if (validateQuery == null || validateQuery.isEmpty()) {
            return true;
        }

        int queryTimeout = validationQueryTimeout <= 0 ? timeout : validationQueryTimeout;

        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.createStatement();
            stmt.setQueryTimeout(queryTimeout);
            rs = stmt.executeQuery(validateQuery);
            return true;
        } finally {
            JdbcUtils.close(rs);
            JdbcUtils.close(stmt);
        }
    }

3.3 SqlServer的验证方式

    public boolean isValidConnection(final Connection c, String validateQuery, int validationQueryTimeout) throws Exception {
        if (c.isClosed()) {
            return false;
        }

        Statement stmt = null;

        try {
            stmt = c.createStatement();
            if (validationQueryTimeout > 0) {
                stmt.setQueryTimeout(validationQueryTimeout);
            }
            stmt.execute(validateQuery);
            return true;
        } catch (SQLException e) {
            throw e;
        } finally {
            JdbcUtils.close(stmt);
        }
    }

4、配置Druid连接池

 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
     <!-- jdbc_url是AnalyticDB for MySQL集群的连接地址URL,可以在控制台的集群信息页面获取连接URL。-->
     <property name="url" value="${jdbc_url}" />

     <!-- jdbc_user是AnalyticDB for MySQL集群中的用户账号:高权限账号或者普通账号。 -->
     <property name="username" value="${jdbc_user}" />

     <!-- jdbc_password是AnalyticDB for MySQL集群中用户账号对应的密码。 -->
     <property name="password" value="${jdbc_password}" />

     <!-- 配置初始化连接池大小、最小连接数、最大连接数。 -->
     <property name="initialSize" value="5" />
     <property name="minIdle" value="10" /> 
     <property name="maxActive" value="20" />

     <!-- 配置获取连接等待超时的时间。 -->
     <property name="maxWait" value="60000" />

     <!-- 配置一个连接在连接池中的最小生存时间、最大生存时间,超过最大生存时间会被移除,单位毫秒。 -->
     <property name="minEvictableIdleTimeMillis" value="600000" />
     <property name="maxEvictableIdleTimeMillis" value="900000" />

     <!-- 配置间隔多久进行一次检测,检测需要关闭的空闲连接,单位毫秒。 默认是60s,太长可能会导致无法及时检测到连接中断。  -->
     <property name="timeBetweenEvictionRunsMillis" value="2000" />

     <!-- 配置从连接池获取连接时,当连接空闲时间大于timeBetweenEvictionRunsMillis时是否检查连接有效性,true每次都检查;false不检查。 -->
     <property name="testWhileIdle" value="true" />

     <!-- 配置从连接池获取连接时和向连接池归还连接时,是否检查连接有效性。  -->
     <!-- 每次获取或归还连接都检测太频繁,除非特别重要或网络特别不可靠等情况,建议用testWhileIdle + timeBetweenEvictionRunsMillis代替。  -->
     <property name="testOnBorrow" value="false" />
     <property name="testOnReturn" value="false" />

     <!-- 配置是否定期探活、探活间隔。 -->
     <property name="keepAlive" value="true" />
     <property name="keepAliveBetweenTimeMillis" value="3000" />  <!-- 默认120s。 -->

     <!-- 探活、验证链接有效性的查询,新版本默认使用mysqlPing代替。 -->
     <property name="validationQuery"  value="select 1" />
     
     <!-- 配置监控统计拦截的filters。 -->
     <property name="filters" value="stat" /> 
 </bean>     
0

评论区