HikariCP 未在 close() 上关闭连接(连接泄漏)

Posted

技术标签:

【中文标题】HikariCP 未在 close() 上关闭连接(连接泄漏)【英文标题】:HikariCP not closing connections on close() (Connection Leak) 【发布时间】:2019-05-26 11:35:43 【问题描述】:

我正在使用 HikariCP 3.3.1 和 PostgreSQL。但是我在关闭连接时遇到问题,在 Hikari 配置中,我将最大池大小设置为 15,将最小空闲连接设置为 5,但是在使用数据库几分钟后,我发现连接没有关闭,它们堆叠越来越多(现在几乎 100 个空闲连接)。

我的连接器类:

Connector.java

public class Connector implements IConnector 
private static HikariConfig config = new HikariConfig();
private static HikariDataSource ds;

static 
    config.setDriverClassName(org.postgresql.Driver.class.getName());
    config.setJdbcUrl("jdbc:postgresql://localhost:5432/vskDB");
    config.setUsername("postgres");
    config.setPassword("root");
    config.setMinimumIdle(5);
    config.setMaximumPoolSize(15);
    config.setConnectionTimeout(20000);
    config.setIdleTimeout(300000);
    ds = new HikariDataSource(config);


public Connection getConnection() 
    log.info("getConnection() invoked");
    try 
        return ds.getConnection();
     catch (SQLException e) 
        log.error("Can't get connection from DataSource.");
        log.error(e.getMessage());
        System.out.println(e.getMessage());
    
    return null;


Connector() 


这是我的 DAO 类(简化版): UserDAO.java

public class UserDatabaseDAO implements UserDAO 
    private Connector connector = new Connector();
    private Connection dbConnection;

    @Override
    public void removeUser(Long id) 
        try 
            dbConnection = connector.getConnection();
            if (dbConnection == null)
                throw new ConnectException();

            PreparedStatement preparedStatement = dbConnection.prepareStatement("DELETE FROM users WHERE user_id = ?");
            preparedStatement.setLong(1, id);
            preparedStatement.execute();

         catch (SQLException | ConnectException e) 
            log.error("Can't remove user from database");
            log.error(e.getMessage());
            System.out.print(e.getMessage());
         finally 
            try 
                dbConnection.close();
             catch (SQLException e) 
                log.error("Can't close connection");
                log.error(e.getMessage());
                System.out.print(e.getMessage());
            
        
    

Here 我发现有关 Hikari 的一些事实的问题:您必须在 HikariCP 为您提供的连接实例上调用 close()

也许我的dbConnection.close() 不起作用,因为它只是 Hikari 在getConnection() 方法中给我的 Connection 的副本。

【问题讨论】:

有点不清楚你在问什么。像 HikariCP 这样的连接池的全部意义在于保持连接打开以供重用。所以连接保持打开并不意外,这是重点。当您在来自 HikariCP 数据源的连接上调用 close() 时,它会返回到连接池,连接并未物理关闭。 我注意到您的所有连接都有不同的 PID,这表明您正在启动多个永不结束的进程。 @MarkRotteveel 我使用了 Java 7 中的 try-with-resource,现在这里只有 15 个空闲连接,因为我在 Max pool size 中设置。但它们不会在 idleTimeout 后关闭。我已将 maxLifetime 和 idleTimeout 设置为默认值。可能是因为自动提交还是其他原因?顺便说一句,Connection 的“后端启动”有时会发生变化。 【参考方案1】:

你也忘了关闭PreparedStatement

try 
       if (preparedStatement != null) 
            preparedStatement.close();
       
       if (dbConnection != null) 
            dbConnection.close();
       

立即释放此 Statement 对象的数据库和 JDBC 资源,而不是等待它自动关闭时发生。通常最好在使用完资源后立即释放资源,以避免占用数据库资源。

【讨论】:

嗯,也许你是对的。我可以使用 try-with-multiple 资源吗?在我的情况下,使用 connection.getConnection() 和 PreparedStatement 声明来关闭它们? @EDWIN 有可能,您可以try通过将返回 Connection PreparedStatement 的方法放入 try-with-resources 块中 可以这样做吗:try (Connection dbConnection = connector.getConnection(); PreparedStatement preparedStatement = dbConnection.prepareStatement("SELECT * FROM users WHERE user_id = ?") ) 如果connector.getConnection() 返回 null,我认为在这个地方会出现 NullPointerException dbConnection.prepareStatement @EDWIN 所以让方法抛出 ConnectException 而不是 null。另见***.com/questions/33703905/… @EDWIN 是的,如果您正在使用,但我没有在您的问题中看到

以上是关于HikariCP 未在 close() 上关闭连接(连接泄漏)的主要内容,如果未能解决你的问题,请参考以下文章

为啥“Quarkus”选择“Agroal”而不是“HikariCP”作为首选数据源和连接池实现? [关闭]

HikariCP - MYSQL 连接关闭后不允许操作。可能考虑使用更短的 maxLifetime 值

服务器关闭连接时客户端是不是也需要调用close

HikariCP为什么快?

Spring Boot 青睐的数据库连接池HikariCP为什么是史上最快的?

会话未在nifi自定义处理器内关闭异常