记一次Spring Boot应用中数据库连接阻塞问题排查过程
记一次Spring Boot应用中数据库连接阻塞问题排查过程
最近在项目开发过程中,发现一个Spring Boot服务在执行某个涉及数据库的操作时,程序线程会突然阻塞,不会继续执行,也没有明显的异常提示。经过详细排查,最终确定了问题根本原因是数据库连接未被正确释放导致连接池耗尽。以下是具体的排查过程与问题定位和解决方法。
问题现象
Spring Boot项目调用某个服务接口执行数据库结构更新操作时,程序始终在获取数据库连接(dataSource.getConnection()
)这一行被阻塞,没有任何异常抛出,也无法继续响应。
问题排查过程
第一步:确认连接池状态
使用数据库命令 SHOW PROCESSLIST
和查询语句 SELECT * FROM information_schema.processlist WHERE Host LIKE "%192.168.1.109%" AND DB = "db_app"
,发现每执行一次请求,数据库连接数就会增加一个,直到达到连接池上限,随后接口便卡住,无法响应。
第二步:分析连接池耗尽原因
连接池耗尽的典型原因有:
- 存在连接泄漏,导致连接池未及时回收连接。
- 存在慢查询或长时间事务未提交。
进一步通过数据库的监控命令(例如SHOW PROCESSLIST
)确认,数据库层面不存在明显的慢SQL或锁等待问题。
第三步:定位Java代码问题
重新检查Java代码发现,存在手动调用dataSource.getConnection()
而未释放连接的问题,示例如下:
// 存在连接泄漏的错误示例
Connection conn = dataSource.getConnection();
UpdateTableService updateService = new UpdateTableService(conn);
updateTableService.updateTable(...);
// 此处并未关闭连接!
由于上述代码中的连接获取后未关闭,导致连接长期占用,最终导致连接池资源被耗尽,后续请求无法获得连接。
解决方案
明确指出问题后,改用推荐的try-with-resources结构来确保连接自动释放:
try (Connection conn = dataSource.getConnection()) {
UpdateTableService updateService = new UpdateTableService(conn);
updateService.updateTable(...);
} catch (SQLException e) {
log.error("执行数据库操作失败", e);
}
这样一来,当try块执行完毕后,连接会自动释放回连接池,防止连接泄漏。
为什么@Transactional没有自动释放连接?
值得注意的是,Spring的@Transactional
注解并不会自动帮你关闭手动获取的Connection
。它只会管理事务的提交和回滚,连接本身的释放需要显式地完成或通过Spring管理的数据访问方式(如JdbcTemplate
)来实现自动管理。
dataSource.getConnection()获取的Connection调用close是关闭链接还是放回池子中
调用Connection.close()
方法时,如果连接是从数据库连接池(如 HikariCP、Druid、C3P0 等)获取的,实际上并不会真正地关闭物理连接,而是将连接释放回连接池中,以供其他请求复用。
具体来说:
-
连接池的连接:
Connection conn = dataSource.getConnection(); conn.close(); // 并非真正关闭,而是归还给连接池
在这种场景下,连接池会管理连接的生命周期和复用,提升数据库操作效率。
-
非连接池的连接(例如直接从DriverManager获取):
Connection conn = DriverManager.getConnection(url, user, password); conn.close(); // 真正关闭物理连接
这里会直接关闭与数据库的物理连接。
建议
- 推荐使用Spring封装的数据库工具,如
JdbcTemplate
或JPA,自动管理连接释放。 - 如果必须手动管理连接,一定要在finally或者try-with-resources中显式关闭连接。
总结
数据库连接阻塞问题的根本原因往往是连接泄漏导致连接池耗尽。Spring的@Transactional注解管理的是事务边界而非具体连接。手动获取连接一定要明确释放,否则将可能引发严重的生产问题。
参考链接:
- MySQL SHOW PROCESSLIST 用法与示例
- information_schema.processlist 表说明