JDBC MySql 连接池实践避免连接池耗尽

Posted

技术标签:

【中文标题】JDBC MySql 连接池实践避免连接池耗尽【英文标题】:JDBC MySql connection pooling practices to avoid exhausted connection pool 【发布时间】:2011-01-19 18:28:33 【问题描述】:

我在 GlassFish 上有一个 Java-JSF Web 应用程序,我想在其中使用连接池。因此,我创建了一个 application 作用域 bean,它与 Connection 实例一起为其他 bean 服务:

public class DatabaseBean 

    private DataSource myDataSource;

    public DatabaseBean() 
        try 
            Context ctx = new InitialContext();
            ecwinsDataSource = (DataSource) ctx.lookup("jdbc/myDataSource");
         catch (NamingException ex) 
            ex.printStackTrace();
        
    

    public Connection getConnection() throws ClassNotFoundException, SQLException, InstantiationException, IllegalAccessException 
        Connection connection = myDataSource.getConnection();
        System.out.println("Succesfully connected: " + connection);
        //Sample: Succesfully connected: com.sun.gjc.spi.jdbc40.ConnectionHolder40@7fb213a5
        return connection;
    

这样连接池很快就会被填满;在“db-related”视图中导航几次后,应用程序停止并显示以下内容:

RAR5117:无法从连接池 [mysql_testPool] 获取/创建连接。原因:使用中的连接等于 max-pool-size 和过期的 max-wait-time。无法分配更多连接。 RAR5114:分配连接时出错:[分配连接时出错。原因:使用中的连接等于 max-pool-size 和过期的 max-wait-time。无法分配更多连接。] java.sql.SQLException:分配连接时出错。原因:使用中的连接等于 max-pool-size 和过期的 max-wait-time。无法分配更多连接。

我正在关闭所有方法中的连接和其他资源。应用程序通过独立连接运行一切正常。

我做错了什么?任何提示或建议将不胜感激。

【问题讨论】:

【参考方案1】:

该异常表示应用程序代码泄漏数据库连接的典型案例。您需要确保以完全相同的方法在try-with-resources 块中获取关闭所有它们(ConnectionStatement ResultSet)按照正常的 JDBC 习惯进行阻塞。

public void create(Entity entity) throws SQLException 
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL_CREATE);
    )  
        statement.setSomeObject(1, entity.getSomeProperty());
        // ...
        statement.executeUpdate();
    

或者当您不在 Java 7 上时,在 try-finally 块中。在finally 中关闭它们将保证它们在出现异常时也被关闭。

public void create(Entity entity) throws SQLException 
    Connection connection = null;
    PreparedStatement statement = null;

    try  
        connection = dataSource.getConnection();
        statement = connection.prepareStatement(SQL_CREATE);
        statement.setSomeObject(1, entity.getSomeProperty());
        // ...
        statement.executeUpdate();
     finally 
        if (statement != null) try  statement.close();  catch (SQLException logOrIgnore) 
        if (connection != null) try  connection.close();  catch (SQLException logOrIgnore) 
    

是的,即使使用连接池,您仍然需要自己关闭连接。初学者中的一个常见错误是他们认为它会自动处理关闭。这不正确。连接池即返回一个包装的连接,它在 close() 中执行以下操作:

public void close() throws SQLException 
    if (this.connection is still eligible for reuse) 
        do not close this.connection, but just return it to pool for reuse;
     else 
        actually invoke this.connection.close();
    

不关闭它们会导致连接不会被释放回池以供重用,因此它将一次又一次地获取新的连接,直到数据库用完连接,这将导致您的应用程序崩溃。

另见:

How often should Connection, Statement and ResultSet be closed in JDBC? Is it safe to use a static java.sql.Connection instance in a multithreaded system? Closing JDBC Connections in Pool

【讨论】:

感谢您的详尽回答,这真的很有帮助!我用 app-modifications 编辑了我的评论。 再次感谢您提供的重要信息!【参考方案2】:

如果您需要 JDBC 连接池,为什么不依赖现有的呢? AFAIK,JDBC 连接池或多或少被认为是这些 Java 应用程序服务器中的标准功能,而 IMO,如果您只是对创建应用程序感兴趣,您不应该自己构建它。

这是一个可以帮助您入门的链接: http://weblogs.java.net/blog/2007/09/12/totd-9-using-jdbc-connection-pooljndi-name-glassfish-rails-application

您可能应该做的是找出如何让您的应用程序使用 jndi 从池中获取连接。

【讨论】:

他已经这样做了?他已经在 appserver 中配置了一个连接池数据源。他只是没有按照异常正确关闭资源。

以上是关于JDBC MySql 连接池实践避免连接池耗尽的主要内容,如果未能解决你的问题,请参考以下文章

在 Hibernate/C3P0 中处理连接池耗尽并避免死锁

tomcat JDBC连接池c3p0连接资源耗尽导致tomcat实例对应的app移动端无法访问。

2.5 JDBC连接池

Spring JDBC 连接池最佳实践

什么是数据库连接泄漏

weblogic连接池