mysql连接超时是后端开发中高频出现的问题,表现形式可能是业务日志里的Communications link failure、Connection timed out,也可能是慢查询后抛出的Read timed out。要有效解决,必须先分清超时发生在TCP建连阶段、连接空闲回收阶段,还是SQL执行阶段,不同阶段的成因与对策完全不同。

一、明确超时的具体类型
从客户端视角看,mysql连接超时通常分为三类。第一类是建立连接时就超时,例如DNS解析慢、mysql服务没起来、防火墙拦截了3306端口,此时报错多为Connection timed out或Unknown MySQL server host。第二类是连接建立后闲置一段时间,再次使用旧连接被发现已断开,抛出Communications link failure。第三类是连接正常但执行某条SQL耗时过长,超过了客户端或服务端允许的等待上限,表现为Read timed out或Statement cancelled。
只有先看清楚异常堆栈里的关键字,才能定位该改哪一层配置。不少团队一遇到超时就调大连接池数量,结果完全没触达真实原因。下面分别给出每类的排查与解决思路,并配合参数说明和代码样例。
1. TCP建连阶段超时
这类问题多和环境有关。可以用telnet或nc命令直接探测端口是否通,排除网络层故障。若网络正常但仍连不上,需检查mysql的bind-address是否只绑了127.0.0.1,以及用户权限里的host限制。
在JDBC里,可以通过connectTimeout参数控制建连超时,单位毫秒。以下示例将建连超时设为五秒,避免默认无限等待:
String url = "jdbc:mysql://127.0.0.1:3306/demo?connectTimeout=5000&socketTimeout=10000";
try (Connection conn = DriverManager.getConnection(url, "user", "pass")) {
// 正常执行查询
} catch (SQLException e) {
// 捕获超时异常并处理
e.printStackTrace();
}
2. 空闲连接被回收导致超时
mysql服务端有两个关键参数:wait_timeout和interactive_timeout,单位秒,控制非交互和交互连接的空闲回收时间。如果客户端用了长生命周期的连接池,但连接闲置超过该值,服务端会主动断开,客户端下次借用此连接就会失败。
解决方式有两种:一是调大服务端wait_timeout,二是在连接池开启空闲检测与保活。以HikariCP为例,可配置连接存活探测:
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/demo");
config.setUsername("user");
config.setPassword("pass");
// 连接空闲超过三百分钟会被回收,这里设比服务端小的值
config.setIdleTimeout(300000);
config.setKeepaliveTime(120000);
config.setMaxLifetime(600000);
HikariDataSource ds = new HikariDataSource(config);
这样连接池会在连接被服务端踢掉之前主动回收或保活,业务层基本感知不到断开。注意maxLifetime应小于服务端wait_timeout,留出缓冲。
3. SQL执行阶段超时
当某条语句扫描行数过多或锁等待久,可能触发客户端socketTimeout或服务端net_read_timeout、net_write_timeout。JDBC的socketTimeout涵盖从发请求到收完响应的总时长,设得太小容易误杀正常慢查询,太大则故障时不敏感。
对于单条语句还可使用Statement.setQueryTimeout设置秒级上限,由驱动内部定时取消:
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement()) {
stmt.setQueryTimeout(20); // 超过20秒抛SQLException
ResultSet rs = stmt.executeQuery("SELECT * FROM big_table WHERE cond = 1");
while (rs.next()) {
// 处理结果
}
}
同时应在服务端用慢查询日志定位耗时SQL,靠加索引或拆批处理根治,而不是单纯加超时时间。
二、连接池层面的常见误区
很多系统用 Druid 或 HikariCP,却没配testOnBorrow,借出连接时不做校验,拿到死连接才报错。开启校验会增加少量开销,但能拦住绝大多数空闲断连问题。
另外,连接池最大大小不是越大越好。mysql默认max_connections有限,盲目调大池子会造成服务端连接爆满,反而让新连接超时。应按业务并发量估算,通常池子大小在十到五十之间足够中等服务使用。
Druid校验配置示例
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/demo"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
<property name="testOnBorrow" value="true"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="maxActive" value="20"/>
</bean>
上面的validationQuery用极轻量的SELECT 1探测连接可用性,testOnBorrow在借出时跑一次,能明显降低Communications link failure出现频率。
三、系统参数对照表
下面整理常见超时参数及作用层,方便对照调整:
| 参数名 | 所在层 | 含义 | 建议值 |
|---|---|---|---|
| connectTimeout | JDBC URL | 建立TCP连接超时 | 3000至5000毫秒 |
| socketTimeout | JDBC URL | SQL收发总超时 | 10000至30000毫秒 |
| wait_timeout | mysql服务端 | 空闲连接回收时间 | 300至28800秒 |
| maxLifetime | 连接池 | 连接最大存活期 | 小于wait_timeout |
| setQueryTimeout | Statement | 单语句取消时限 | 依业务定 |
实际处理mysql连接超时,核心是先抓异常类型,再对准参数层,最后用连接池保活与SQL优化收尾。盲目重启或扩池只会掩盖问题,定期看慢日志与服务端超时配置才是长效办法。