Mysql异常之Communications link failure

本文记录了一次线上数据库抖动引发的异常告警,通过排查发现是由于一个磁盘故障导致的长操作超时。作者详细分析了`CommunicationsException`的触发原因,并通过代码重现验证了socketTimeout设置对问题的影响。后续计划深入研究mysqldriver源码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前天线上出现数据数据抖动,收到大量异常告警,都要疯了,数据库出现大量异常,肯定第一件事就是看下数据库监控数据,发现有一个从分片执行时间有一个50多秒的操作,这里肯定有问题了,第一时间找了dba,经过排查发现这个分片的磁盘出现了问题,dba加入了一个新的分片,有问题的分片作摘除处理,这件事才告一段落,但是通过查看服务器日志,发现抛出了大量的CommunicationsException , 关键描述信息:Communications link failure.......Last packet sent to the server was 3003 ms ago。

看到3003这个时间好熟悉啊,我们数据连接属性配置了socketTimeout=3000,mysql客户端与数据库的通信使用的socket,客户端向mysql server发送命令,mysql客户端有程序保护机制,如果由于网络原因或者数据库故障导致数据始终没有返回处理结果,为了防止客户端一直无限等待mysql server返回结果而造成线程阻塞浪费资源,那么socketTimeout参数就起作用了,超过socketTimeout还没有相应,那么就将请求结束,所以抛出了CommunicationsException。为了验证这个异常,在本地环境还原了这个场景。

1.原始数据库操作,构建一个慢写入操作

public class Test {
    private static Connection conn;
    private static Statement stmt;
    private static boolean rs;
    public static void main(String[] args) throws SQLException {
        try
        {
            Class.forName("com.mysql.jdbc.Driver");
            // 配置socketTimeout=500,单位是ms
            conn= DriverManager.getConnection("jdbc:mysql://192.168.88.130:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT&socketTimeout=500","root","123456");
            stmt=conn.createStatement();
        }
        catch(SQLException e)
        {
      
            e.printStackTrace();
        }
        catch(ClassNotFoundException e)
        {
            
            e.printStackTrace();
        }
        StringBuilder sql = new StringBuilder("insert into test1(a) values");
        // 构建一个慢写入操作,使造作时间超过socketTimeout=500ms        
        for (int i = 0; i < 100000;i++) {
            sql.append("(" + i + "),");
        }
        sql.append("(10000000);");
        rs = stmt.execute(sql.toString());
    }
}

2.果然抛出了异常

Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 537 milliseconds ago.  The last packet sent successfully to the server was 512 milliseconds ago.
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	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:403)
	at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:990)
	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3517)
	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3417)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3860)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2524)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2675)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2465)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2439)
	at com.mysql.jdbc.StatementImpl.executeInternal(StatementImpl.java:829)
	at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:729)
	at com.jd.poporder.test.Test.main(Test.java:39)
Caused by: java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:101)
	at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:144)
	at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:174)
	at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2966)
	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3427)
	... 9 more

Process finished with exit code 1

3.从异常可以看出触发异常的原因是:Read timed out

今天先写到这里,改天有时间需要趴下mysql driver源码看下具体的策略是什么样的,等我吧! 

### 解决方案 当遇到 `Communications link failure` 错误时,通常是因为网络连接不稳定或配置不正确引起的。针对阿里云 MySQL 数据库的具体情况,可以采取以下措施来解决问题: #### 修改 JDBC URL 参数 在 JDBC URL 中添加特定参数可以帮助稳定连接并解决 SSL 认证问题。建议修改 JDBC URL 如下所示[^2]: ```java jdbc:mysql://<host>:<port>/<database>?useSSL=false&serverTimezone=Asia/Shanghai&connectTimeout=60000&socketTimeout=60000 ``` #### 配置超时设置 为了防止因长时间未响应而导致的通信链路失败,在 JDBC URL 中增加连接超时和套接字超时参数是非常重要的[^4]。 #### 检查防火墙和安全组规则 确保服务器上的防火墙以及阿里云的安全组允许来自客户端 IP 地址的入站流量到达指定端口(通常是 3306)。这一步骤对于远程访问尤其重要[^5]。 #### 查看 MySQL 超时变量 通过执行 SQL 查询命令 `SHOW VARIABLES LIKE '%timeout%'` 来获取当前会话中的各种超时设定,并根据实际情况调整这些值以适应应用需求。 #### 开启 MySQL 服务 确认目标机器上已经启动了 MySQL 服务。如果是在本地开发环境中,则需保证该服务处于运行状态;如果是云端实例,则应遵循服务商提供的指南进行操作。 以上方法综合运用能够有效减少甚至消除 `Communications link failure` 的发生频率,提高应用程序与数据库之间的稳定性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值