`

Mysql的一种CommunicationsException异常

    博客分类:
  • SQL
阅读更多

问题背景:

一个项目需要启动一个定时器任务,隔断时间访问一下DB,后端DB使用的是Mysql。之前测试的时候,启动程序,自动运行没有问题,逻辑正常。周五晚上走后,启动程序,按照之前配置的Cron,周末程序会自动执行。周一来了之后查看DB的数据发现有问题,于是查看服务器的程序日志,发现爆出了如下错误(非关键错误省略。。。):

 

Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 82,661,051 milliseconds ago.  The last packet sent successfully to the server was 82,661,051 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
        at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1122)
        at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3317)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1941)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2114)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2696)
        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2105)
        at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2264)
        at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:93)
        at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)
        at org.hibernate.loader.Loader.getResultSet(Loader.java:1812)
        at org.hibernate.loader.Loader.doQuery(Loader.java:697)
        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
        at org.hibernate.loader.Loader.doList(Loader.java:2232)
        ... 30 more
Caused by: java.net.SocketException: Broken pipe
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
        at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3298)
        ... 41 more

问题原因:

其实上面的提示中已经给出了一部分的简要说明,简单来说就是: 程序启动时,在跟DB首次交互时,获得了相应的DB Connection资源,从而进行正常的DB读写操作。但是在下次进行DB读写时(我的定时任务本身设置的时间间隔是24小时),应用程序认为这个连接是可以正常使用的(程序执行过一次之后没有退出,这个连接从来并没有被释放掉),但实际上,这个连接已经坏掉了,因为Mysql本身已经把这个连接标记为timeout了。于是,应用程序“傻乎乎”的在这个已经坏掉的数据通道上发起对DB的读写请求,但是Mysql已经对这些请求不买账了。。。

 

为啥呀,mysql到底认为这个连接空闲多长时间算过期啊?

这个可以通过查看mysql的配置文件,看看是否有对这个时间做过特殊的配置,我的场景下在64位linux服务器上部署的mysql服务器,这个配置文件在:在/etc/my.cnf

如果你打开这个文件,发现并没有如下这行配置:

 

wait_timeout=xxx (这里xxx是数据,单位为秒)

说明你并没有对这个timeout做过特殊配置,通常Mysql默认的配置是8小时。你也可以在登陆进入mysql之后,通过如下命令确认一下:

 

show global variables like 'wait_timeout';

结合上面我的程序的配置(24小时执行一次),上面的问题就好解释了:24小时之后程序再次对DB进行读写操作时,Mysql单方已经认为之前connection已经timeout了(停止活动了8小时以上就认为过期了)。

如何解决:

Google了一下,发下下面两篇文章还比较靠谱:

http://www.databasesandlife.com/automatic-reconnect-from-hibernate-to-mysql/

http://javacrazyer.iteye.com/blog/721393/

基本思路也比较清晰,从上面我们分析问题的过程中,我们也可以想到如下两点:

1、既然Application持有了无效的DB connection自己不知道,那就让App变聪明点,自己在真正使用之前都去先确认一下自己的持有的connection是否有效再进行处理呗。这个方向的解决方案就是上面的第一个链接提到的。当然里面借助了C3P0。

2、既然App需要长时间的持有DB connection以备后面继续使用(24小时之后再次使用),那就让mysql的默认连接空闲的timeout加长到大于这个时间呗。理论上,保证过期时间大于这个connection的再次使用时间间隔就哦了。这个就是上面第二个连接的具体解决方案了。

另外,关于这里的第二点,需要注意的一点是,网上的多数配置说明都是将这个值调整的比默认8小时更短,比如这里http://www.serveridol.com/2012/04/13/mysql-interactive_timeout-vs-wait_timeout/ 有比较详细的说明。主要原因也是因为,长时间让DB保持连接不释放,对DB本身的资源也是一种严重的消耗。我的使用场景是定时器,没有前台用户访问,所以这种问题不大。但是如果你的使用场景是前端供用户使用的App,那这个恐怕就需要小心测试了。

分享到:
评论

相关推荐

    mysql 异常com.mysql.jdbc.CommunicationsException

    在Java应用程序中使用MySQL数据库时,可能会遇到`com.mysql.jdbc.CommunicationsException`这一异常。该异常通常意味着与MySQL服务器的通信出现问题,例如网络中断、服务器端主动断开连接等。 #### 异常详情 本次...

    mysql-connector-java-5.1.18.rar

    此外,特定于MySQL的异常如`CommunicationsException`和`DataAccessException`也可能出现,需要适当地处理。 10. **优化与性能** 对于大规模应用,可能需要调整MySQL服务器和Connector/J的配置参数,如连接超时、...

    mysql-connector-java-5.0.6

    9. **异常处理**:当数据库操作发生错误时,会抛出`SQLException`及其子类,如`CommunicationsException`、`DataAccessException`等,开发者需要捕获并适当地处理这些异常。 10. **性能优化**:MySQL Connector/J ...

    JDBC实现Mysql自动重连机制的方法详解

    4. **调整MySQL配置**:另一种解决方案是调整MySQL服务器的`wait_timeout`值,使其适应你的应用需求。你可以通过修改MySQL的配置文件(如`my.cnf`)并重启服务来实现。但这可能不是最佳实践,因为它会影响所有数据库...

    Mail服务器应用

    - **CommunicationsException**:这是MySQL JDBC驱动程序抛出的一个异常类,通常表示客户端与数据库服务器之间的通信出现问题。 - **可能的原因**: - 网络连接中断或不稳定。 - 数据库服务器未响应或已关闭。 - ...

    数据库驱动

    MySQL是一种广泛使用的开源关系型数据库管理系统,它的JDBC驱动程序允许Java应用通过网络连接到MySQL服务器,执行SQL查询,处理结果集,并进行事务操作。安装MySQL驱动(通常为.jar文件)到类路径后,就可以使用`...

    JDBC学习笔记(含思维导图).zip

    Java Database Connectivity (JDBC) 是Java平台中用于与关系数据库进行交互的一种标准接口。它由Sun Microsystems(现为Oracle公司)开发,为Java开发者提供了访问数据库的能力,支持多种数据库,包括MySQL、Oracle...

    JDBC(ORACLE)

    Java Database Connectivity (JDBC) 是Java编程语言中用于与关系数据库交互的一种标准接口。它是Java平台的标准部分,允许程序员使用Java编写数据库应用程序。Oracle是世界上最广泛使用的数据库管理系统之一,提供了...

    Java语言SQL接口 JDBC编程技术

    12. **异常处理**:在JDBC编程中,常见的异常有SQLException及其子类,如CommunicationsException、DataIntegrityViolationException等,需要妥善捕获和处理。 13. **连接池**:为了优化性能和资源管理,开发者通常...

    JDBC-EXAMPLE

    JDBC提供了一种标准的API,允许Java开发者通过编写Java代码来访问和操作数据库。无论数据库是MySQL、Oracle、SQL Server还是其他类型,JDBC都能帮助我们建立连接、执行SQL语句、处理结果集,从而实现数据的存取和...

Global site tag (gtag.js) - Google Analytics