连接池泄漏原因

时间:2016-10-06 14:18:38

标签: java mysql jdbc connection-pooling

我一直在遇到(我认为)连接池问题。 具体来说,我的日志显示消息:

  

org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject $ AbandonedObjectCreatedException:以下代码创建的池时间[时间]尚未返回池中

我已经检查了堆栈跟踪中列出的日志显示但是无法找到罪魁祸首(我总是关闭ResultSetPreparedStatementConnection的方法。每种方法结束)。

我有一个执行两个查询的方法,也许我没有正确执行它。

它的布局如下:

ConnectionPool pool = ConnectionPool.getInstance();
Connection connection = pool.getConnection();
PreparedStatement ps = null;
PreparedStatement rowsPs = null;
ResultSet rs = null;
ResultSet rowsRs = null;

String query = "SELECT SQL_CALC_FOUND_ROWS ...";
String totalRowsQuery = "SELECT FOUND_ROWS() AS RowCount";

try {
    ps = connection.prepareStatement(query);
    [set ps params]
    rs = ps.executeQuery();
    [process rs]

    rowsPs = connection.prepareStatement(totalRowsQuery);
    rowsRs = rowsPs.executeQuery();
    [process rowsRs]
} catch (SQLException e) {
    [handle e]
} finally {
    DBUtil.closeResultSet(rs);
    [close rowsRs]
    [close ps]
    [close rowsPs]
    [close connection]
}

DBUtils方法的示例是:

public static void closeResultSet(ResultSet rs)
{
    try
    {
        if (rs != null)
            rs.close();
    }
    catch (SQLException sqle)
    {
        sqle.printStackTrace();
    }
}

此方法的总体布局是否正常?我应该以不同方式处理连接吗?或者是其他导致错误记录的方法?

谢谢。

其他信息

我还得到SQLException

  

java.sql.SQLException:连接com.mysql.jdbc.JDBC4Connection @ [某个号码]已关闭

行:rowsPs = connection.prepareStatement(totalRowsQuery);

意味着先前某处,连接已关闭。 我没有在任何地方明确关闭连接。 是否有可能某个其他数据访问方法在某种程度上关闭此方法中的连接? (pool.getConnection()调用dataSource.getConnection()

更新: 我已尝试按照建议使用try-with-resources,但问题仍然存在。

上面第一个代码段中引用的ConnectionPool类:

public class ConnectionPool 
{

    private static ConnectionPool pool = null;
    private static DataSource dataSource = null;

    public synchronized static ConnectionPool getInstance()
    {

        if ( pool == null ) {
            pool = new ConnectionPool();
        }
            return pool;
    }

    private ConnectionPool()
    {
        try {
            InitialContext ic = new InitialContext();
            dataSource = (DataSource) 
                    ic.lookup([jdbc/dbName]);
        }

        catch (Exception e) {
            e.printStackTrace();
        } 
    }

    public Connection getConnection()
    {
        try {
            return dataSource.getConnection();
        }
        catch (SQLException sqle) {
            sqle.printStackTrace();
            return null;
        }
    }
    public void freeConnection(Connection c)
    { 
        try {
            c.close();
        }
        catch (SQLException sqle) {
            sqle.printStackTrace();
        }
    }
}

更多来源: 我的资源池元素:

<Resource auth="Container" driverClassName="com.mysql.jdbc.Driver"  
            logAbandoned="true" maxActive="100" maxIdle="30" maxWait="10000" 
            removeAbandonedOnBorrow="true" 
            removeAbandonedTimeout="60" type="javax.sql.DataSource" 
            testWhileIdle="true" testOnBorrow="true" 
            validationQuery="SELECT 1 AS dbcp_connection_test"/>

更新 我打开了慢查询日志,但是,尽管再次抛出Exception,慢查询日志也不会记录任何内容(查询时间不超过10秒)。

所以看起来并不是查询花费的时间超过60秒。

仍然不确定导致这种情况的原因。

4 个答案:

答案 0 :(得分:2)

使用池化JDBC连接时的一般经验法则:

  1. 不要在每个连接上一次打开多个ResultSet。在打开第二个ResultSet之前关闭第一个ResultSet及其关联的Statement。

  2. 始终按照您创建它们的相反顺序关闭资源:create(s1) - &gt; execute(r1) - &gt; close(r1) - &gt; close(s1) - &gt; create( S2) - &GT;执行(R 2) - &GT;靠近(R 2) - &GT;关闭(S2)

  3. 确保长时间运行的查询处理不超过池允许的最长连接生存期。

  4. 即使您没有超过最大连接生命周期,由于网络链路中断很少,连接也可能会丢失。

答案 1 :(得分:2)

这两行占用60多秒,因此连接池决定您的连接被放弃并关闭它。稍后您的代码尝试使用连接,但它已被连接池调用。

rs = ps.executeQuery();
[process rs]

如果您确定它不是很长的executeQuery(),请使用连接/线程/请求ID和[process rs]部分的时间进行调试打印。如果你看到这个部分有超过50秒的时间,你应该优化[进程rs]或读取数据并在处理之前将它们存储在内存中。

答案 2 :(得分:1)

mysql服务器是否会超时连接?它也可能是一个内核,如果连接长时间处于空闲状态,底层的tcp连接可能会被杀死。

答案 3 :(得分:1)

尝试将removeAbandonedTimeout值减少到20或15之类。你的maxWait是10秒,但如果连接被放弃,那么你在等待60秒才返回它们。

请注意,这不是一个解决方案,只是一个测试,看看你的连接是否真的挂起。