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などから取得する同時接続の数に厳密な上限を設定します。

コードに接続リークがあるかどうかを確認する1つの方法は、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*

これは今のところ十分に機能します。何が起こっているかというと、10 個の接続に到達すると、Tomcat が放棄された接続 (アイドル時間 > 3) をチェックしているということです。これは、最大接続数に達するたびにバッチ ジョブで実行されます。これに関する潜在的な問題は、同時に10を超えるクエリを実行する必要がある場合です(私に固有のものではありません)。重要なことは、removeAbandonedTimeout が maxWait 未満であることです。

これは起こっているべきことですか?つまり、これはプールが動作する方法ですか? 少なくとも私には、何か (接続) が壊れるまで待ってから修正するように思われます。多分私はまだそれを取得していません。

于 2008-09-12T12:00:24.003 に答える
1

接続が適切に閉じられず、「スリープ」モードでスタックするという問題

これは実際には半分しか正しくありませんでした。

私が実際に遭遇した問題は、各アプリがデータベース サーバーへの新しい接続を定義していたことです。したがって、すべての接続を閉じるたびに、アプリ A は WEB.xml 構成ファイルに従って一連の新しい接続を作成し、問題なく実行されます。アプリ B も同じことを行います。問題は、サーバーが定義した制限まで取得しようとする独立したプールであることです。それは私が推測する一種の競合状態です。そのため、アプリ A が接続を終了すると、タイムアウトが経過するまで接続を再度使用するために待機し、アプリ A が接続を終了してプールに戻る必要があるにもかかわらず、接続を必要とするアプリ B がリソースを拒否されます。タイムアウトが経過すると、接続が解放され、B (または C など) が再び接続できるようになります。

たとえば、制限が 10 (mySQL プロファイルの制限) で、各アプリが最大 10 を使用するように構成されている場合、20 回の接続試行が行われます。明らかにこれは悪い状況です。

解決策は、RTFM を使用して、接続の詳細を適切な場所に配置することです。これにより、共有投稿が苦痛になりますが、それを回避する方法があります (コンテキストから他の xml ファイルにリンクするなど)。

明確にするために、接続の詳細を各アプリのWEB.xmlに入れましたが、それについて戦いました。

于 2010-11-08T07:31:07.880 に答える
0

@binilが見逃したことの1つは、例外の場合に結果セットを閉じていないことです。ドライバーの実装によっては、これにより接続が開いたままになる場合があります。result.close()呼び出しをfinallyブロックに移動します。

于 2008-09-10T12:42:52.173 に答える
0

私はあなたと同じ構成を使用しています。mysql Administrator(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 に答える