【问题标题】:HikariCP try-with-resources Connection leakingHikariCP try-with-resources 连接泄漏
【发布时间】:2019-01-18 14:45:43
【问题描述】:

我正在做一些事情,我需要从 MariaDB 中提取数据(使用 HikariCP),然后通过 Redis 发送。最终,当我尝试从数据库中提取数据时,连接将开始泄漏。这只会随着时间的推移而突然发生。

这是泄漏开始时的完整日志:https://hastebin.com/sekiximehe.makefile

这是一些调试信息:

21:04:40 [INFO] 21:04:40.680 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Before cleanup stats (total=6, active=2, idle=4, waiting=0)

21:04:40 [INFO] 21:04:40.680 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After cleanup  stats (total=6, active=2, idle=4, waiting=0)

21:04:40 [INFO] 21:04:40.682 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.mariadb.jdbc.MariaDbConnection@4b7a5e97

21:04:40 [INFO] 21:04:40.682 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After adding stats (total=7, active=2, idle=5, waiting=0)

21:05:05 [INFO] 21:05:05.323 [HikariPool-1 housekeeper] WARN  com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for org.mariadb.jdbc.MariaDbConnection@52ede989 on thread Thread-272, stack trace follows
java.lang.Exception: Apparent connection leak detected
        at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123)
        at us.survivewith.bungee.database.FetchPlayerInfo.run(FetchPlayerInfo.java:29)
        at java.lang.Thread.run(Thread.java:748)

21:05:10 [INFO] 21:05:10.681 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Before cleanup stats (total=7, active=2, idle=5, waiting=0)

21:05:10 [INFO] 21:05:10.681 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After cleanup  stats (total=7, active=2, idle=5, waiting=0)

21:05:39 [INFO] 21:05:39.352 [HikariPool-1 housekeeper] WARN  com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for org.mariadb.jdbc.MariaDbConnection@3cba7850 on thread Thread-274, stack trace follows
java.lang.Exception: Apparent connection leak detected
        at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123)
        at us.survivewith.bungee.database.FetchPlayerInfo.run(FetchPlayerInfo.java:29)
        at java.lang.Thread.run(Thread.java:748)

这里是 FetchPlayerInfo.run() 方法:

@Override
public void run()
{
    String select = "SELECT `Rank`,`Playtime` FROM `Players` WHERE PlayerUUID=?;";

    // This is line 29. How can this possibly be causing a leak?
    try(Connection connection = Database.getHikari().getConnection())
    {
        // Get the data by querying the Players table
        try(PreparedStatement serverSQL = connection.prepareStatement(select))
        {
            serverSQL.setString(1, player);

            // Execute statement
            try(ResultSet serverRS = serverSQL.executeQuery())
            {
                // If a row exists
                if(serverRS.next())
                {
                    String rank = serverRS.getString("Rank");

                    Jedis jPublisher = Redis.getJedis().getResource();
                    jPublisher.publish("playerconnections", player + "~" + serverRS.getInt("Playtime") + "~" + rank);
                }
                else
                {
                    Jedis jPublisher = Redis.getJedis().getResource();
                    jPublisher.publish("playerconnections", player + "~" + 0 + "~DEFAULT");
                }
            }
        }
    }
    catch(SQLException e)
    {
        //Print out any exception while trying to prepare statement
        e.printStackTrace();
    }
}

这就是我设置数据库类的方式:

/**
 * This class is used to connect to the database
 */
public class Database
{
    private static HikariDataSource hikari;

    /**
     * Connects to the database
     */
    public static void connectToDatabase(String address,
                                         String db,
                                         String user,
                                         String password,
                                         int port)
    {
        // Setup main Hikari instance
        hikari = new HikariDataSource();
        hikari.setMaximumPoolSize(20);
        hikari.setLeakDetectionThreshold(60 * 1000);
        hikari.setDataSourceClassName("org.mariadb.jdbc.MariaDbDataSource");
        hikari.addDataSourceProperty("serverName", address);
        hikari.addDataSourceProperty("port", port);
        hikari.addDataSourceProperty("databaseName", db);
        hikari.addDataSourceProperty("user", user);
        hikari.addDataSourceProperty("password", password);
    }

    /**
     * Returns an instance of Hikari.
     * This instance is connected to the database that contains all data.
     * The stats table is only used in this database every other day
     *
     * @return The main HikariDataSource
     */
    public static HikariDataSource getHikari()
    {
        return hikari;
    }

这就是我调用 FetchPlayerInfo 类的方式:

new Thread(new FetchPlayerInfo(player.getUniqueId().toString())).start();

编辑:

使用 Database 类中的同步 getConnection() 方法后问题仍然存在。

【问题讨论】:

  • 我明白你的意思了......我唯一的猜测是 getConnection() 在某种程度上不是线程安全的?
  • @moilejter 即使随着时间的推移会发生这种情况,而且不是每次调用该方法时,这仍然是您的猜测吗?
  • 在看似随机的时间失败是它可能是某个地方的竞争条件的线索之一 - 你必须恰到好处地让它失败......它很容易测试 - 写Database.getConnection() 作为同步方法,只调用 getHikari().getConnection() - 然后使用它,看看问题是否消失......
  • 这怎么可能是竞争条件呢? FetchPlayerInfo 类是唯一访问 Database.getHikari().getConnection() 的东西,而且它一次只被调用一次。我只是误解了什么是比赛条件吗?
  • 啊!好吧,我以为你实际上是在启动一堆线程来运行它,而不仅仅是一个......

标签: java sql redis mariadb hikaricp


【解决方案1】:

Jedis 也是 JedisPool 的 resource,您应该关闭:

 /// Jedis implements Closeable. Hence, the jedis instance will be auto-closed after the last statement.
 try (Jedis jedis = pool.getResource()) {

【讨论】:

  • 很好的收获。这会导致连接泄漏吗?
【解决方案2】:

什么版本的 HikariCP?泄漏可能实际上不是泄漏。当连接离开池的时间超过阈值时会报告泄漏,他实际上可能稍后返回。较新版本的 HikariCP 将记录“未泄露”的连接。

编辑:我几乎 100% 确定 HikariCP 中没有竞争条件。这种情况非常简单,而且 HikariCP 被太多用户(数百万)使用,这样一个根本性的缺陷以前从未出现过。

查看上面的代码和生成的日志,唯一有意义的是外部 try-catch 内部的调用之一挂起(阻塞)。我建议在条件发生时获取堆栈转储,以查找FetchPlayerInfo.run() 内部是否有线程阻塞。

【讨论】:

  • 我使用的是 2.7.6 版。不过,我遇到的不是泄漏吗?
  • 我也想知道这一点 - 但 coe 似乎没有任何类型的循环或在获得连接的 try 块内等待 - 所以我预计它会运行一点点同时,然后完成 - 而不是阈值启动所需的时间......
  • @moilejter 我不确定你在哪里得到“启动阈值所需的时间”,问题中的配置将阈值设置为 60 秒。
  • 啊!你是对的 - 我在考虑图书馆的默认值,我认为大约是 8 小时(但我可能会完全考虑其他事情:-()
  • 我正在使用 Hikari 3.2.0 Mysql 和 Spring Boot JPA 库,我每天都会遇到几次连接泄漏异常。使用的配置:leakDetectionThreshold=30000 minimumIdle=5 maximumPoolSize=20 idleTimeout=10000 maxLifetime=30000 connectionTimeout=30000 JPA 应该在完成后关闭连接,你为什么认为我有这样的错误?请注意,我的查询很简单,不应超过 ms。会不会是与 DB 的连接问题?
猜你喜欢
  • 1970-01-01
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2020-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多