连接池泄漏原因

9q78igpj  于 2021-06-25  发布在  Mysql
关注(0)|答案(4)|浏览(403)

我一直(我想)在连接池方面遇到问题。具体来说,我的日志显示以下消息:
org.apache.tomcat.dbcp.pool2.impl.defaultpooledobject$ForwardedObjectCreatedException:由以下代码创建的池对象[时间]尚未返回到池
我检查了日志显示的堆栈跟踪中列出的方法,但找不到罪魁祸首(我总是关闭 ResultSet , PreparedStatement 以及 Connection 在每个方法的末尾)。
我有一个执行两个查询的方法,也许我没有正确地执行它。
其布局如下:

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@[some number]已关闭
在线路上: rowsPs = connection.prepareStatement(totalRowsQuery); 这意味着在之前的某个地方,连接是关闭的。我没有明确关闭任何地方的连接。是否有可能调用的其他数据访问方法正在以某种方式关闭此方法中的连接( pool.getConnection() 电话 dataSource.getConnection() )
更新:我已尝试使用建议的资源,但问题仍然存在。
上面第一个代码段中引用的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秒。
仍然不确定是什么原因造成的。

kknvjkwl

kknvjkwl1#

尝试将RemoveBandonedTimeout值减少到20或15左右。您的maxwait是10秒,但是如果连接被放弃,那么您需要等待60秒才能返回它们。
请注意,这不是一个解决方案,只是一个测试,看看你的连接是否真的挂起。

ki1q1bka

ki1q1bka2#

mysql服务器是否超时连接?这也可能是内核的问题,如果连接长时间处于空闲状态,底层tcp连接可能会被终止。

at0kjp5o

at0kjp5o3#

这两条线路需要60秒以上的时间,因此连接池决定放弃您的连接并将其关闭。稍后,您的代码尝试使用连接,但它已被连接池取消。

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

由于您确定它不是long executequery(),请使用connection/thread/request id和[process rs]部分的时间进行调试打印。如果您看到这个部分有50多秒,您应该优化[process rs]或读取数据,并在处理之前将它们存储在内存中。

lo8azlld

lo8azlld4#

使用池jdbc连接时的一般经验法则是:
不要在每个连接上同时打开多个结果集。在打开第二个结果集之前,请关闭第一个结果集及其关联的语句。
始终按创建资源的相反顺序关闭资源:创建(s1)->执行(r1)->关闭(r1)->关闭(s1)->创建(s2)->执行(r2)->关闭(r2)->关闭(s2)
确保长时间运行的查询处理未超过池允许的最大连接生存期。
即使未超过最大连接生存期,也可能因为网络链接中的小中断而丢失连接。

相关问题