【问题标题】:HikariCP detects connectionleak even though all of my code has a call to close method即使我的所有代码都调用了 close 方法,HikariCP 也会检测到连接泄漏
【发布时间】:2024-01-01 13:36:02
【问题描述】:

我在我们的 Web 应用程序中使用 HikariCP,突然之间,我得到了这个异常:

    [Hikari housekeeper (pool HikariPool-0)] WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for connection com.mysql.cj.jdbc.ConnectionImpl@3b82d04f, stack trace follows

java.lang.Exception:检测到明显的连接泄漏

我已经检查并确保我所有的 sql 代码都调用了 close 方法。

这是我的 HikariConfig:

    private HikariConfig hikariConfig() {
    HikariConfig config = new HikariConfig();
    config.setDriverClassName(CLASS_FOR_NAME);
    config.setJdbcUrl(HOST);
    config.setUsername(USER);
    config.setPassword(PASS);
    config.addDataSourceProperty("cachePrepStmts", "true");
    config.addDataSourceProperty("prepStmtCacheSize", "250");
    config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
    config.setLeakDetectionThreshold(TimeUnit.SECONDS.toMillis(30));
    config.setValidationTimeout(TimeUnit.MINUTES.toMillis(1));
    config.setMaximumPoolSize(40);
    config.setMinimumIdle(0);
    config.setMaxLifetime(TimeUnit.MINUTES.toMillis(2)); // 120 seconds max life time
    config.setIdleTimeout(TimeUnit.MINUTES.toMillis(1)); // minutes
    config.setConnectionTimeout(TimeUnit.MINUTES.toMillis(5)); // millis
    config.setConnectionTestQuery("/* ping */ SELECT 1");

    return config;
}

这是我的查询的样子:

   public ArrayList<LocationType> getLocationTypes() {
    ArrayList<LocationType> locationTypes = new ArrayList<>();
    Connection connection = null;
    PreparedStatement pstmt = null;
    ResultSet resultSet = null;

    try {
        connection = DataSource.getInstance().getConnection();

        pstmt = connection.prepareStatement("SELECT\n" +
                "  location_type_id,\n" +
                "  location_type_name\n" +
                "FROM tablename;");

        resultSet = pstmt.executeQuery();

        while (resultSet.next()) {
            locationTypes.add(new LocationType(resultSet.getInt("location_type_id"), resultSet.getString("location_type_name")));
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (resultSet != null)
            try {
                resultSet.close();
            } catch (SQLException e) {
            }

        if (pstmt != null)
            try {
                pstmt.close();
            } catch (SQLException e) {
            }

        if (connection != null)
            try {
                connection.close();
            } catch (SQLException e) {
            }
    }
    return locationTypes;
}

我已经尝试增加 connectionLeakDetection、最大连接和最小空闲,但都没有解决问题。

我已经读到这可能是由机器(资源不足)和连接被关闭引起的,但是我认为这些都不会导致问题。

我注意到我的代码中的一些长查询现在被检测为连接泄漏,即使我没有调用他们的方法。希望大家帮忙。

这是堆栈跟踪:

    at com..database.DataSource.getConnection(DataSource.java:148)
at com.database.databaseServices.MileageReportService.MileageService.getMonthlySummaryBySID(MileageService.java:27)
at com.views.reports.mileage.MileageReport.lambda$generateReport$61446b05$1(MileageReport.java:103)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)

这一行的唯一一行 - MileageService.java:27,是我对 DB 的查询,它在 finally 调用中有一个 close 语句。

【问题讨论】:

  • 你用的是哪个版本的光CP?
  • 你能分享一下你发布的日志行后面的堆栈跟踪吗?
  • 我使用的是 2.4.3。我今天刚刚升级到 2.5.1,但仍然遇到同样的异常。
  • 我在我的问题@Ivan 中添加了堆栈跟踪
  • @JRojo 你知道MileageService. getMonthlySummaryBySID 有多少时间吗?那个方法似乎是罪魁祸首,而不是getLocationTypes

标签: java vaadin hikaricp


【解决方案1】:

升级到最新的 HikariCP。

HikariCP 将连接报告为泄漏,因为从借用到关闭所用的时间比指定的 LeakDetectionThreshold 长。

2.4.9 及更高版本,它记录连接被报告为泄漏后是否返回。

另外,看看你的配置,我会调整属性如下 (前 4 到 1 分钟,最后到 5):

config.setLeakDetectionThreshold(TimeUnit.MINUTES.toMillis(1));
config.setConnectionTimeout(TimeUnit.MINUTES.toMillis(1));
config.setValidationTimeout(TimeUnit.MINUTES.toMillis(1));
config.setIdleTimeout(TimeUnit.MINUTES.toMillis(1));

config.setMaxLifetime(TimeUnit.MINUTES.toMillis(5));

HTH

【讨论】:

  • 您好 Nitin,感谢您的回答。我用您建议的那个更新了我的 hikariconfig,但仍然发生异常。我目前正在使用 hikari 2.5.1。
  • 在等待了大约 5 分钟后,我看到了连接已返回池的日志。尽管我的查询的执行时间不需要一秒钟,但我不确定为什么在我的 java 代码中实现它之后需要一段时间。
  • 日志也显示连接 ID。可能会在您认为需要第二而不是 5 分钟的查询周围放置调试语句。