You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

验证方式

  1. 模拟连续三次主键冲突请求,观察Hikari连接池的连接数变化(可以通过Spring Boot Actuator的/hikari端点查看监控数据)。
  2. 检查异常发生后,连接是否被正确回滚并放回连接池,没有出现连接泄漏的情况。

内容的提问来源于stack exchange,提问作者Jishnu Prathap

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 01:25:29