SpringBoot2 hikari关于 Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl处理

Posted 云梦逐浪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot2 hikari关于 Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl处理相关的知识,希望对你有一定的参考价值。

项目启动不报错,如果静默15分钟没有数据库操作就报上述错误WARN 不影响程序运行

Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@16244d67 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.

发现SpringBoot2开始配置文件有所变化

spring:
  #数据库
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    password: root
    username: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      max-lifetime: 30000 # 不能小于30秒,否则默认回到1800秒

springboot hikari数据库死链 出现异常

最近系统间歇性很慢,比如登录,经常超时、无反应,查看log出现下述两种异常,
异常情况1

The last packet successfully received from the server was 995,202 milliseconds ago. The last packet sent successfully to the server was 995,202 milliseconds ago.
    at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
    at com.mysql.cj.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl.java:2056)
    at com.zaxxer.hikari.pool.ProxyConnection.setAutoCommit(ProxyConnection.java:388)
    at com.zaxxer.hikari.pool.HikariProxyConnection.setAutoCommit(HikariProxyConnection.java)
    at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:284)
    ... 44 common frames omitted

异常情况2

Caused by: java.sql.SQLNonTransientConnectionException: No operations allowed after connection closed.
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:73)
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:73)
    at com.mysql.cj.jdbc.ConnectionImpl.setNetworkTimeout(ConnectionImpl.java:2490)
    at com.zaxxer.hikari.pool.PoolBase.setNetworkTimeout(PoolBase.java:550)
    at com.zaxxer.hikari.pool.PoolBase.isConnectionAlive(PoolBase.java:165)
    at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:179)
    ... 30 common frames omitted

怀疑数据库链接存在死链,后端是springboot使用的默认数据库连接池hikari,配置如下

spring:
  datasource:
    driver: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://x.x.x.x:3306/dbname?useUnicode=true&characterEncoding=utf-8
    username: xx
    password: xxxx
    hikari:
      maximumPoolSize: 20
      maxLifetime: 60000
      minimumIdle: 10
      connectionTestQuery: SELECT 'x'

查看源码得知,com.zaxxer.hikari.pool.HikariPool#getConnection(long)获取连接时,判断连接是否存活,

com.zaxxer.hikari.pool.PoolBase#isConnectionAlive
boolean isConnectionAlive(final Connection connection)
 
     try 
        setNetworkTimeout(connection, validationTimeout);

        final int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;
        //如果不配置connectionTestQuery,才会使用jdbc校验机制
        if (isUseJdbc4Validation) 
           return connection.isValid(validationSeconds);
        
    
 com.zaxxer.hikari.pool.PoolBase#PoolBase
    PoolBase(final HikariConfig config)
   
      this.config = config;
      this.isUseJdbc4Validation = config.getConnectionTestQuery() == null;
     
   
   com.mysql.cj.jdbc.ConnectionImpl#isValid
   public boolean isValid(int timeout) throws SQLException 
        //发送心跳包
        pingInternal(false, timeout * 1000);
        return true;
        
    

继续往下跟到了com.mysql.cj.protocol.a.NativeProtocol#sendCommand

public final NativePacketPayload sendCommand(Message queryPacket, boolean skipCheck, int timeoutMillis) 
    //发送心跳包,debug得知发送的十六进制0x0e,也就是数字14
    send(queryPacket, queryPacket.getPosition());


总结

至此真相大白,因为配置了connectionTestQuery,所以没有使用jdbc的校验机制,修改方法就是去掉connectionTestQuery,jdbc的校验机制是在获取连接时,校验合法性,比定时校验效率要高;
另外,jdbc的校验相比上层库校验效率更高,因为是tcp层的心跳包而不是mysql引擎解析的sql语句,所以druid、hikari层的校验可以去掉了。

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于SpringBoot2 hikari关于 Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl处理的主要内容,如果未能解决你的问题,请参考以下文章

springBoot 整合 hikari

SpringBoot系列之Hikari连接池

Hikari连接池

HiKariCP和Druid对比使用整理自测

Hikari数据池在Spring启动应用程序中关闭

如何在 Spring Boot 2.0 上将默认 hikari cp 替换为 tomcat 池