3

Seam&Hibernate(JDBCからSQLServer)で動作するSeamWebアプリケーションがあります。

正常に動作していますが、高負荷(JMeterによるストレステスト)では、いくつかLockAcquisitionExceptionまたはがありOptimisticLockExceptionます。

これLockAquisitionExceptionは、SQLServerException 「トランザクション(プロセスID 64)が別のプロセスとのロックリソースでデッドロックし、デッドロックの犠牲者として選択されました。トランザクションを再実行してください」が原因です。

次に、そのようなトランザクションを再実行するためのSeamInterceptorを作成しましたLockAquisitionException

@AroundInvoke
public Object aroundInvoke(final InvocationContext invocationContext) throws Exception {
    if (instanceThreadLocal.get() == null && isMethodInterceptable(invocationContext)) {
        try {
            instanceThreadLocal.set(this);

            int i = 0;
            PersistenceException exception = null;
            do {
                try {
                    return invocationContext.proceed();
                } catch (final PersistenceException e) {
                    final Throwable cause = e.getCause();
                    if (!(cause instanceof LockAcquisitionException)) {
                        throw e;
                    }
                    exception = e;
                    i++;
                    if (i < MAX_RETRIES_LOCK_ACQUISITION) {
                        log.info("Swallowing a LockAcquisitionException - #0/#1", i, MAX_RETRIES_LOCK_ACQUISITION);
                        try {
                            if (Transaction.instance().isRolledBackOrMarkedRollback()) {
                                Transaction.instance().rollback();
                            }
                            Transaction.instance().begin();
                        } catch (final Exception e2) {
                            throw new IllegalStateException("Exception while rollback the current transaction, and begining a new one.", e2);
                        }
                        Thread.sleep(1000);
                    } else {
                        log.info("Can't swallow any more LockAcquisitionException (#0/#1), will throw it.", i, MAX_RETRIES_LOCK_ACQUISITION);
                        throw e;
                    }
                }
            } while (i < MAX_RETRIES_LOCK_ACQUISITION);

            throw exception;

        } finally {
            instanceThreadLocal.remove();
        }
    }
    return invocationContext.proceed();
}

最初の質問:このインターセプターは正しく機能すると思いますか?

グーグルで調べて、Alfrescoここでフォーラムトークを使用)を見ると、BonitaOrchestraStaleObjectStateExceptionにはそのようなトランザクションを再実行する方法がいくつかあり、たとえば(私の原因)のように、はるかに多くの例外をキャッチしていますOptimisticLockException

私の2番目の質問は次のとおりです:(StaleObjectStateException行が別のトランザクションによって更新または削除された(または保存されていない値のマッピングが正しくなかった)」)の場合、データベースと@Versionフィールドとの同期の問題であるため、通常はトランザクションを再実行することはできませんではない ?たとえば、Alfrescoがそのような例外によって引き起こされたそのようなトランザクションを再実行しようとするのはなぜですか?

編集:LockAcquisitionExceptionによって引き起こされたためSQLServerException、私はウェブ上のいくつかのリソースを見てきました、そして私が私のコードを再確認する必要があるとしても、それはとにかく起こる可能性があるようです...ここにリンクがあります:

Microsoftでさえ、「デッドロックを最小限に抑えることはできますが、完全に回避することはできません。そのため、フロントエンドアプリケーションはデッドロックを処理するように設計する必要があります。」

4

1 に答える 1

2

実際、私はついに有名な「トランザクション(プロセスID 64)が別のプロセスとのロックリソースでデッドロックになり、デッドロックの犠牲者として選択されました。トランザクションを再実行する」を回避する方法を見つけました。

ですから、私は実際には私の質問に答えませんが、私が見たものとそれをどうやって行うことができるかを説明します。

最初は、行ロックをページロックに変換してデッドロックを生成する「ロックエスカレーションの問題」があると思いました(JMeterテストは、行の選択中に削除/更新を行うシナリオで実行されますが、削除と更新は実行されませんselectと同じ行である必要はありません)。

そこで、SQL2005のロックエスカレーションとSQL Serverのロックエスカレーションによって引き起こされるブロッキングの問題を解決する方法(MSによる)を読み、最後にsp_lockを使用してSQLServerのパフォーマンスの問題を診断します

しかし、私がロックエスカレーションの状況にあるかどうかを検出しようとする前に、私はそのページに落ちます:http: //community.jboss.org/message/95300。これは「トランザクション分離」について説明しており、SQLServerには「スナップショット分離」と呼ばれる特別なものがあります。

次に、SQL ServerとHibernateでのスナップショットアイソレーションの使用を見つけ、スナップショットアイソレーションの使用(MSによる)を読みました。

そこで、最初にデータベースで「スナップショットアイソレーションモード」を有効にしました。

ALTER DATABASE [MY_DATABASE]
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE [MY_DATABASE]
SET READ_COMMITTED_SNAPSHOT ON

次に、JDBCドライバーのトランザクション分離を次のように定義する必要がありました4096...「5.1.6分離レベルの設定」の「Hibernatein Action」という本を読むと、次のようになります。

Hibernateは、管理対象環境のアプリケーションサーバーによって提供されるデータソースから取得された接続の分離レベルを変更しないことに注意してください。アプリケーションサーバーの構成を使用して、デフォルトの分離を変更できます

そこで、JBoss DataSourcesの設定(JBoss 4用)を読み、最後にdatabase-ds.xmlファイルを編集してこれを追加しました:

<local-tx-datasource>
    <jndi-name>myDatasource</jndi-name>
    <connection-url>jdbc:sqlserver://BDDSERVER\SQL2008;databaseName=DATABASE</connection-url>
    <driver-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver-class>
    <user-name>user</user-name>
    <password>password</password>
    <min-pool-size>2</min-pool-size>
    <max-pool-size>400</max-pool-size>
    <blocking-timeout-millis>60000</blocking-timeout-millis>
    <background-validation>true</background-validation>
    <background-validation-minutes>2</background-validation-minutes>
    <idle-timeout-minutes>15</idle-timeout-minutes>
    <check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
    <prefill>true</prefill>
    <prepared-statement-cache-size>75</prepared-statement-cache-size>
    <transaction-isolation>4096</transaction-isolation>
</local-tx-datasource>

もちろん、最も重要な部分はです<transaction-isolation>4096</transaction-isolation>

そして、デッドロックの問題はもう発生しません!...だから私の質問は今では多かれ少なかれ役に立たない...しかしおそらく誰かが本当の答えを持っているかもしれない!

于 2011-07-20T14:23:28.303 に答える