数据库连接池的选择和CommunicationsException

背景,本人多个项目最近用到了多个数据源,但是偶尔会出现如下异常:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

1 druid:mysql异常重启导致的CommunicationsException

项目一直正常,突然早晨反馈,系统不可用了,看日志内容如下:
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
        at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3052)
        at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:597)
        ... 15 more
[ERROR]-[2019-08-28 09:15:39]-[DruidDataSource.run():2001]-[Druid-ConnectionPool-Create-1341527551] create connection error, url: jdbc:mysql://gaway-009
:3306/bi?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false, errorCode 0, state 08S01
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 4,620 milliseconds ago.  The last packet sent successfully to the server was 649 milliseconds ago.
        at sun.reflect.GeneratedConstructorAccessor153.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1121)
        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3603)
        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3492)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4043)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2809)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2758)
        at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1612)
        at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3930)
        ... 132 more
Caused by: java.net.SocketException: Connection reset
        at java.net.SocketInputStream.read(SocketInputStream.java:209)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)
        at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)
        at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
        at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3049)
        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3503)
        ... 140 more
[ERROR]-[2019-08-28 09:15:39]-[DruidDataSource.run():2001]-[Druid-ConnectionPool-Create-994566274] create connection error, url: jdbc:mysql://gateway-06:
3306/bireports, errorCode 0, state 08S01
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

没有办法,重启服务ok,后来发现是因为夜里mysql被异常重启了,换句话说异常重启后库连接一直无法建立,本人用的连接池配置如下:
   
       
       
       
       

       
       
       
       

       
       
       
       
       
       
       
       
       
       
   


java中代码如下
DruidDataSource druidDS = (DruidDataSource) DruidDataSourceFactory.createDataSource(conf);
druidDS.setBreakAfterAcquireFailure(true);
druidDS.setConnectionErrorRetryAttempts(5);
问题:夜里mysql异常重启,但是druid似乎没有发现,并且没有进行重连,但是页面操作使用数据库连接就会报上面的异常,怎么让druid在mysql重启的时候自动获取连接呢 ?


经过本人查阅资料和测试,现在确定有两种解决方案
1 设置druidDS.setBreakAfterAcquireFailure(true); 注意好像在配置文件中直接配置不会生效,必须采取java赋值的方式
原因如下:
BreakAfterAcquireFailure是用于失败重连,默认为false,但是在我的项目配置中配置为true。
后来druid的官网查看了发现true表示向数据库请求连接失败后,就算后端数据库恢复正常也不进行重连,客户端对pool的请求都拒绝掉.false表示新的请求都会尝试去数据库请求connection.默认为false。
原文链接:https://blog.csdn.net/weixin_40547924/article/details/92985008


setBreakAfterAcquireFailure(false)后,如果一直重试会导致,一直打印错误日志。
breakAfterAcquireFailure=false是一种方式,但是建议的方式设置错误连接重试时间,即timeBetweenConnectErrorMillis=600000,减少错误日志又能在mysql恢复的时候


2 设置,笔者其实已经设置,但是发现依旧不好用,这是因为
这种方案涉及到你使用的druid和mysql connnector版本,下面的两个版本是不可以的,应该是druid的bug,不会生效:

    com.alibaba
    druid
    1.0.27


    mysql
    mysql-connector-java
    5.1.24

生效的实例版本为:

    com.alibaba
    druid
    1.1.16


    mysql
    mysql-connector-java
    8.0.11

testWhileIdle为true时,会定时对线程池中的链接进行validateObject校验,对无效的链接进行关闭后,会调用ensureMinIdle,适当建立链接保证最小的minIdle连接数。
注意:上面问题版本更新后可能会报异常,如果你的jdbc.url中包含zeroDateTimeBehavior=convertToNull,需要convertToNull修改为CONVERT_TO_NULL

2 dbcp:mysql空闲超时导致的CommunicationsException

问题:部分数据源 早晨使用的时候连接不可用,多刷几次则又可以使用;错误信息如下:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
注意此时场景和上面问题不同,这里是每天早晨都会数据源偶尔失效,而mysql服务并没有重启,原因分析如下:
查看mysql版本和非活跃超时时间:select version();
如果版本大于4,autoReconnect=true 无效
查看和连接mysql时间有关的系统变量:show variables like '%timeout%'
1)interactive_timeout:
参数含义:服务器关闭交互式连接前等待活动的秒数。
参数默认值:28800秒(8小时)
(2)wait_timeout:
参数含义:服务器关闭非交互连接之前等待活动的秒数。
在线程启动时,根据全局wait_timeout值或全局interactive_timeout值初始化会话wait_timeout值,取决于客户端类型(由mysql_real_connect()的连接选项CLIENT_INTERACTIVE定义)。
参数默认值:28800秒(8小时)
wait_timeout:超时控制的变量,其时间为长度为28800s,就是8个小时,在8个小时之后会断开,需要重新连接,可以在访问的url中使用
jdbc.url=jdbc:mysql://localhost:3306/nd?autoReconnect=true来是连接自动恢复,但这是mysql4及其以下版本使用,在mysql5中已经无效,必须调整系统变量来控制
Mysql5中对wait_timeout和interactive_timeout有两个说明:interactive_timeout:服务器关闭交互式连接前等待活动的秒数。交互式客户端定义为在
mysql_real_connect()中使用CLIENT_INTERACTIVE选项的客户端。又见wait_timeout 
wait_timeout:服务器关闭非交互连接之前等待活动的秒数。在线程启动时,根据全局wait_timeout值或全局interactive_timeout值初始化
会话wait_timeout值,取决于客户端类型(由mysql_real_connect()的连接选项CLIENT_INTERACTIVE定义),又见interactive_timeout 
结论:若要修改的话,需要同时修改这两个变量 
原文链接:https://blog.csdn.net/zouxucong/article/details/53924414


基于上面 autoReconnect=true 无效,超时等待时间为8时,那超时后会发生什么?
MySQL服务器默认的“wait_timeout”是28800秒即8小时,意味着如果一个连接的空闲时间超过8个小时,MySQL将自动断开该连接,而连接池却认为该连接还是有效的(因为并未校验连接的有效性),当应用申请使用该连接时,就会导致上面的报错。而且数据内配置的时候没有 做重连配置

解决:
首先dbcp的性能不太好,所以修改成tomcat jdbc pool,完整配置如下:
   
   
       
       
       
       
       

       

       
       
       
       
       
       
       
       
       
       
       
       
       
   


借鉴
https://www.cnblogs.com/wuyun-blog/p/5679073.html

C3P0、DBCP、Tomcat Jdbc Pool、Druid等

C3p0: 开源的JDBC连接池,实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。单线程,性能较差,适用于小型系统。

DBCP (Database Connection Pool):由Apache开发的一个Java数据库连接池项目, Jakarta commons-pool对象池机制,原本Tomcat使用的连接池组件就是DBCP。预先将数据库连接放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完再放回。单线程,并发量低,性能不好,适用于小型系统。

Druid:Druid是Java语言中最好的数据库连接池,Druid能够提供强大的监控和扩展功能,是一个可用于大数据实时查询和分析的高容错、高性能的开源分布式系统,尤其是当发生代码部署、机器故障以及其他产品系统遇到宕机等情况时,Druid仍能够保持100%正常运行。主要特色:为分析监控设计;快速的交互式查询;高可用;可扩展;Druid是一个开源项目,源码托管在github上。

Tomcat Jdbc Pool:Tomcat在7.0以前都是使用dbcp做为连接池组件,但是dbcp是单线程,为保证线程安全会锁整个连接池,性能较差。Tomcat从7.0开始引入了新增连接池模块叫做Tomcat jdbc pool,基于Tomcat JULI,使用Tomcat日志框架,完全兼容dbcp,通过异步方式获取连接,支持高并发应用环境,超级简单核心文件只有8个,支持JMX,支持XA Connection。连接远程的数据库,而网络质量很差,经常断开连接。用dbcp,经常死锁,Druid在这种恶劣的工况下,也不行。最后用的tomcat7连接池,搞定。

C3p0和DBCP 性能差,而且不再更新,所以不能用
druid和tomcatjdbc性能差别不大,并发量比较大选druid吧,网络不好用tomcatJdbc吧,毕竟和tomcat是一起的。
最推荐使用HikariCP连接池,据说牛的一逼,但是还没用过


HikariCP连接池评价
https://www.jianshu.com/p/15b846107a7c
https://www.jianshu.com/p/d60eb6cf8d36

HikariCP坑:https://www.jianshu.com/p/e6f3e1da4bf0

数据源从druid迁移到HikariCP
https://www.cnblogs.com/leechenxiang/p/9201179.html

你可能感兴趣的:(连接池,mysql,框架,mybatis)