你有没有遇到过这种情况:系统明明跑得好好的,突然就卡了,数据库连接数爆满,重启之后又恢复正常?问题很可能出在连接池的连接状态管理上。
连接池这东西,大家都不陌生。它像一个“连接中介”,提前准备好一堆数据库连接,等程序要用的时候直接拿来用,用完再还回去,省去了频繁创建和销毁连接的开销。但很多人只关心“能不能连上”,却忽略了“连上了是不是真能用”。
连接为什么会“假死”?
网络不是理想的高速公路,它有拥堵、断线、超时。比如数据库服务器临时重启、防火墙中断长连接、网络抖动,都可能导致连接池里的某个连接实际上已经断了,但程序还不知道,以为它还活着。
这时候如果程序从连接池取出这个“僵尸连接”去执行查询,结果就是卡住、超时,甚至整个请求线程被拖死。更糟的是,这种连接如果不及时清理,会越积越多,最终耗尽连接池资源。
怎么判断连接还“活着”?
常见的做法是在连接归还给池之前做一次检测。比如 HikariCP 就支持配置 connectionTestQuery 或使用 JDBC 4 的 isValid() 方法。
dataSource.setValidationTimeout(3000);
dataSource.setConnectionTestQuery("SELECT 1");
但也不能每次用都查一下,太影响性能。所以很多连接池提供了“空闲检测”机制——每隔一段时间检查池子里闲置的连接是否有效。
dataSource.setIdleTimeout(60000);
dataSource.setKeepaliveTime(30000);
上面这段配置的意思是:连接空闲超过 60 秒就可能被回收,每 30 秒主动检查一次空闲连接的存活状态。
别忘了网络层的心跳
有些数据库协议本身支持心跳包,比如 MySQL 的 autoReconnect=false 配合 TCP keepalive。但要注意,操作系统默认的 TCP keepalive 时间往往很长(几小时),等它发现断连早就晚了。
建议在应用层自己控制检测频率,别指望系统自动帮你兜底。
异常处理要“干净利落”
当执行 SQL 报错时,不要急着重试,先判断是不是连接问题。如果是网络异常或连接已关闭,应该把这个连接标记为“不可用”,直接丢掉,而不是还回池里。
try {
executeQuery(connection);
} catch (SQLException e) {
if (isConnectionBroken(e)) {
connection.abort(); // 主动销毁
} else {
connectionPool.returnConnection(connection);
}
}
否则,一个坏连接会在池子里反复流转,变成“毒药连接”,传染整个服务。
监控比配置更重要
光配了一堆参数没用,得看实际效果。你应该能随时查到:当前活跃连接数、空闲连接数、等待连接的线程数、连接创建/销毁速率。
一旦发现活跃连接数持续增长、归还率下降,就得警惕了——很可能有连接泄漏,或者检测机制失效。
就像家里的水管,装了阀门不等于万事大吉,还得定期看看有没有漏水。连接池也一样,状态管理不到位,迟早出事。