Spring Boot Hikari连接池因主键冲突引发连接不可用问题排查
主键冲突引发Hikari连接池耗尽的问题排查与解决
根因分析
你用了try-with-resources管理连接,但主键冲突这类SQL异常,在SQL Server JDBC驱动的实现中,可能导致连接处于未自动回滚的状态。Hikari回收连接时会检查连接有效性,如果连接还挂着未结束的事务,会直接判定连接失效并丢弃,不会放回连接池。连续三次这种异常后,再加上你的Hikari配置存在不合理的地方,连接池里的可用连接会快速被耗尽,最终出现连接超时错误。
看你的配置,max-lifetime设了24小时,远超过SQL Server默认的30分钟连接超时,导致Hikari不会主动回收过期连接,一旦有连接因异常被丢弃,池子里的连接只会越来越少,直到彻底耗尽。
解决步骤
1. 显式控制事务的提交与回滚
虽然try-with-resources会关闭连接,但异常发生时连接可能还处于事务中,必须显式回滚才能让连接正常回到池里。修改代码:
public void insertEvent(EventRequest event) throws Exception { Connection connection = null; CallableStatement call = null; try { connection = this.fenxjdbcTemplate.getDataSource().getConnection(); // 关闭自动提交,手动控制事务 connection.setAutoCommit(false); call = connection.prepareCall("{call dbo.SPBIF_Insert(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"); call.setString(1, event.getXX()); call.setString(2, event.getXXXX()); // 其他参数设置代码 call.execute(); // 正常执行完成后提交事务 connection.commit(); } catch (Exception e) { // 异常时强制回滚事务 if (connection != null && !connection.isClosed()) { try { connection.rollback(); } catch (SQLException rollbackEx) { // 记录回滚异常,不要覆盖原异常 rollbackEx.printStackTrace(); } } throw e; } finally { // 手动关闭资源 if (call != null) { try { call.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
核心是确保异常时事务被回滚,让连接能正常回收。
2. 修正Hikari连接池配置
调整关键参数,避免连接长期不被回收,同时增加连接有效性检测:
datasource: driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc-url: jdbc:sqlserver://url username: db_username password: db_password hikari: max-lifetime: 1740000 # 设为29分钟,小于SQL Server默认的30分钟连接超时 minimum-idle: 5 # 减少最小空闲连接,避免资源浪费 maximum-pool-size: 20 # 根据实际业务量调整,不要盲目设大 idle-timeout: 600000 # 10分钟,空闲连接超时自动回收 connection-timeout: 30000 # 连接请求超时设为30秒 connection-test-query: "SELECT 1" # 每次取出/放回连接时检测有效性
max-lifetime必须小于数据库的连接超时时间,否则数据库会主动断开连接,但Hikari还以为连接可用,导致后续请求报错。connection-test-query能及时发现无效连接并移除,避免连接池里堆积坏连接。
3. 针对性处理主键冲突异常
主键冲突是业务异常,提前捕获可以减少无效的连接占用:
catch (SQLServerException e) { // SQL Server主键冲突的错误码是2627 if (e.getErrorCode() == 2627) { // 记录业务日志,比如重复的事件ID System.err.println("主键冲突,事件ID: " + event.getXX()); } // 执行回滚 if (connection != null && !connection.isClosed()) { try { connection.rollback(); } catch (SQLException rollbackEx) { System.err.println("回滚失败: " + rollbackEx.getMessage()); } } throw e; } catch (Exception e) { // 其他异常统一处理回滚 if (connection != null && !connection.isClosed()) { try { connection.rollback(); } catch (SQLException rollbackEx) { System.err.println("回滚失败: " + rollbackEx.getMessage()); } } throw e; }
验证方式
- 模拟连续三次主键冲突请求,观察Hikari连接池的连接数变化(可以通过Spring Boot Actuator的
/hikari端点查看监控数据)。 - 检查异常发生后,连接是否被正确回滚并放回连接池,没有出现连接泄漏的情况。
内容的提问来源于stack exchange,提问作者Jishnu Prathap
相关产品推荐
相关产品推荐

