4

我有一个使用 JNDI 查找来连接数据库的 web 应用程序。

连接工作正常,返回查询没有问题。我们的问题是连接没有正确关闭并且卡在“睡眠”模式(根据mysql管理员)。这意味着它们变得无法使用,然后我的连接就用完了。

有人可以给我一些关于我可以做些什么来使连接成功返回池的指示。

public class DatabaseBean {

private static final Logger logger = Logger.getLogger(DatabaseBean.class);

private Connection conn;
private PreparedStatement prepStmt;

/**
 * Zero argument constructor
 * Setup generic databse connection in here to avoid redundancy
 * The connection details are in /META-INF/context.xml
 */
public DatabaseBean() {
    try {
        InitialContext initContext = new InitialContext();
        DataSource ds = (DataSource) initContext.lookup("java:/comp/env/jdbc/mysite");
        conn = ds.getConnection();
    }
    catch (SQLException SQLEx) {
        logger.fatal("There was a problem with the database connection.");
        logger.fatal(SQLEx);
        logger.fatal(SQLEx.getCause());
    }
    catch (NamingException nameEx) {
        logger.fatal("There was a naming exception");
        logger.fatal(nameEx);
        logger.fatal(nameEx.getCause());
    }
}

/**
 * Execute a query. Do not use for statements (update delete insert etc).
 *
 * @return A ResultSet of the execute query. A set of size zero if no results were returned. It is never null.
 * @see #executeUpdate() for running update, insert delete etc.
 */

public ResultSet executeQuery() {
    ResultSet result = null;
    try {
        result = prepStmt.executeQuery();
        logger.debug(prepStmt.toString());
    }
    catch (SQLException SQLEx) {
        logger.fatal("There was an error running a query");
        logger.fatal(SQLEx);
    }
    return result;
}

剪辑

public void close() {
    try {
        prepStmt.close();
        prepStmt = null;

        conn.close();
        conn = null;
    } catch (SQLException SQLEx) {
        logger.warn("There was an error closing the database connection.");
    }
}
}

这是在使用数据库连接的 javabean 中。

public LinkedList<ImportantNoticeBean> getImportantNotices() {

    DatabaseBean noticesDBBean = new DatabaseBean();
    LinkedList<ImportantNoticeBean> listOfNotices = new LinkedList<ImportantNoticeBean>();

    try {
        PreparedStatement preStmt = noticesDBBean.getConn().prepareStatement("SELECT pseudonym, message, date_to, date_from " +
                "FROM importantnotices, users " +
                "WHERE importantnotices.username = users.username " +
                "AND NOW() >= date_from AND NOW() <= date_to;");

        noticesDBBean.setPrepStmt(preStmt);
        ResultSet result = noticesDBBean.executeQuery();

        while (result.next()) {
            ImportantNoticeBean noticeBean = new ImportantNoticeBean();

            noticeBean.setAuthor(result.getString("pseudonym"));
            noticeBean.setMessage(result.getString("message"));
            noticeBean.setDateTo(result.getDate("date_to"));
            noticeBean.setDateFrom(result.getDate("date_from"));

            listOfNotices.add(noticeBean);
        }

        result.close();

    } catch (SQLException SQLEx) {
        logger.error("There was an error in ImportantNoticesBean.getImportantNotices()");
        logger.error(SQLEx);
    } finally {
        noticesDBBean.close();
    }
    return listOfNotices;
}

<Context reloadable="true">

    <Resource name="jdbc/mysite"
              auth="Container"
              type="javax.sql.DataSource"
              username="user"
              password="password"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/mysite"
              maxActive="10"
              maxIdle="5"
              maxWait="6000"
              removeAbandoned="true"
              logAbandoned="false"
              removeAbandonedTimeout="20"
            />
</Context>
4

6 回答 6

2

您似乎正确地关闭了连接 - 除了 prepStmt.close() 抛出 SQLException 的情况,我找不到连接泄漏。

您使用的是什么池实现?当你关闭一个连接时,池不需要立即关闭底层的 MySQL 连接——毕竟​​那是连接池的重点!因此,从 MySQL 方面来看,尽管您的应用程序没有使用任何连接,但连接看起来是活跃的;它们可能只是由 TC 连接池持有。

您可能想尝试连接池的设置。要求它在系统空闲时收缩池。或者,要求它定期刷新所有连接。或者,对它从 MySQL 等获得的并发连接数有一个严格的上限。

检查您的代码是否存在连接泄漏的一种方法是强制 ds.getConnection() 始终打开新的物理连接并 conn.close() 释放连接(如果您的连接池具有这些设置)。然后,如果您观察 MySQL 端的连接,您可能能够确定代码是否真的存在连接泄漏。

于 2008-09-10T01:29:31.043 回答
2

这是一个类似的问题 - Tomcat 的连接池设置

这是我对那个问题的回答,它为另一个人解决了这个问题。它也可以帮助你。

Tomcat 文档

DBCP 使用 Jakarta-Commons 数据库连接池。它依赖于 Jakarta-Commons 组件的数量:

* Jakarta-Commons DBCP
* Jakarta-Commons Collections
* Jakarta-Commons Pool

我正在使用相同的连接池的东西,我正在设置这些属性以防止它只是没有通过 tomcat 配置的相同的东西。但是,如果第一件事不起作用,请尝试这些。

testWhileIdle=true
timeBetweenEvictionRunsMillis=300000
于 2008-09-10T13:52:25.657 回答
2

好的,我可能已经排序了。我已将数据库配置资源更改为以下内容:

*SNIP*
maxActive="10"
maxIdle="5"
maxWait="7000"
removeAbandoned="true"
logAbandoned="false"
removeAbandonedTimeout="3"
*SNIP*

这目前工作得很好。发生了什么,afaik,一旦我达到十个连接,那么 Tomcat 就会检查放弃的连接(空闲时间 > 3)。每次达到最大连接数时,它都会在批处理作业中执行此操作。这样做的潜在问题是,如果我需要同时运行 10 个以上的查询(不是我独有的)。重要的是 removeAbandonedTimeout 小于 maxWait。

这是应该发生的事情吗?即这是池应该运行的方式吗?如果是这样的话,至少在我看来,你会等到某些东西(连接)断开后再修复,而不是一开始就不让它“断开”。也许我仍然没有得到它。

于 2008-09-12T12:00:24.003 回答
1

我们的问题是连接没有正确关闭并且卡在“睡眠”模式

这实际上只对了一半。

我遇到的问题实际上是每个应用程序都在定义与数据库服务器的新连接。所以每次我关闭所有连接时,App A 都会根据它的 WEB.xml 配置文件建立一堆新的连接并愉快地运行。应用程序 B 也会这样做。问题是它们是独立的池,它们试图达到服务器定义的限制。我猜这是一种竞争条件。因此,当应用程序 A 完成连接后,它会等待再次使用它们,直到超时过去,而现在需要连接的应用程序 B 被拒绝资源,即使应用程序 A 已完成连接并且应该回到池中。一旦超时过去,连接就会被释放,B(或 C 等)可以再次访问它。

例如,如果限制为 10(mySQL 配置文件限制)并且每个应用程序已配置为最多使用 10 个,则将有 20 次连接尝试。显然,这是一个糟糕的情况。

解决方案是 RTFM 并将连接详细信息放在正确的位置。这确实使共享发布很痛苦,但有一些方法可以解决它(例如从上下文链接到其他 xml 文件)。

明确一点:我将每个应用程序的连接详细信息放在 WEB.xml 中,并为此争论不休。

于 2010-11-08T07:31:07.880 回答
0

@binil 错过的一件事是,在发生异常的情况下,您不会关闭结果集。根据驱动程序的实现,这可能会导致连接保持打开状态。将 result.close() 调用移至 finally 块。

于 2008-09-10T12:42:52.173 回答
0

我正在使用与您相同的配置。如果 mysql 管理员(windows)中的连接显示它处于睡眠模式,则仅表示已入池但未使用。我检查了这个运行测试程序的程序,该程序有多个线程对 Mysql 进行随机查询。如果有帮助,这是我的配置:

        defaultAutoCommit="false"
        defaultTransactionIsolation="REPEATABLE_READ"
        auth="Container"
        type="javax.sql.DataSource"
        logAbandoned="true" 
          removeAbandoned="true"
        removeAbandonedTimeout="300" 
        maxActive="-1"
        initialSize="15"
        maxIdle="10"
        maxWait="10000" 
        username="youruser"
        password="youruserpassword"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://yourhost/yourdatabase"/>
于 2009-07-06T04:50:04.193 回答