2

概要:

同じ行を更新しようとして大量のメッセージが発生し、Oracle デッドロック エラーが発生する高スループット アプリケーションのベスト プラクティスを知りたいです。これらのエラーを回避できないことはわかっていますが、何度も何度も発生するデッドロック エラーによって行き詰まることなく、どのようにして正常に回復するのでしょうか。

詳細:

高スループットの JMS メッセージング アプリケーションを構築しています。本番環境は、2 つの weblogic 11g ノード (それぞれ 6 つの MDB リスナー インスタンスを実行) になります。Oracle データベースで同じ行を更新しようとする約 1000 のメッセージを取得すると、Oracle デッドロック エラー (ORA-00060) が発生していました。ノード間の Java 同期は、標準の Java スレッド API では不可能です (テラコッタなどのサードパーティ ソリューションを使用したくない他のソリューションがない場合を除きます)。

Oracleの「select for update WAIT n secs」ステートメントが役立つことを期待していました。これにより、競合するスレッド(同じ行の)が最初のスレッド(最初に行のロックを取得したスレッド)が処理されるまで数秒待機するようになるためです。 .

「SELECT FOR UPDATE WAIT n」の最初の問題は、待機時間にミリ秒を使用できないことです。これは、アプリケーションのスループットに悪影響を及ぼし始めます。これは、1 秒の WAIT (最小待機時間) を設定するとメッセージの遅延が発生するためです。

次に、weblogic キューの再配信遅延パラメーター (この場合は 30 秒) をいじっています。デッドロック エラーが原因でスレッドが跳ね返るたびに、再試行されるまで 30 秒待機します。

私たちの経験では、1000 件の競合するメッセージがあり、デッドロックが何度も発生し続けるため、多くの状況で処理に永遠に時間がかかります。

現在のアーキテクチャでは、(1000 件の競合するメッセージの場合) 関係なくデッドロック エラーが発生するはずですが、アプリケーションは、ループ メッセージを再試行した後にこれらのエラーから回復するのに十分な回復力を備えている必要があることを理解しています。

ここで何が欠けているか分かりますか? 以前に同様の問題に対処したことがある人はいますか?

このデッドロック状態から回復し、追加のハードウェアをあまり使用せずに適切な時間内にすべてのメッセージを最終的に処理できるように、これを弾力的に機能させることができる設計のアイデアを探しています。

計算の詳細: これらの 1000 のメッセージは、それぞれに関連付けられた数量を持つ 4 つの異なるポジション タイプの 4 つのオブジェクトを作成します。これらの数量は、(ポジション タイプに応じて) 4 つの異なるスロットに統合する必要があります。これらの 4 つの個別のスロットが個別のスレッドによって更新されているときに、デッドロックが発生しています。競合状態を回避するために、データベース行に適用される前に、これらの個々の更新を特定の順序で並べ替えています。

4

2 に答える 2

0

MDBに関しては

  1. メッセージを消費し、処理されたメッセージの量のデルタを含むインスタンス変数を更新します (MDB は複数のメッセージにわたってインスタンス変数の状態を保持できます)。

  2. 同じ MDB 内の@Scheduleメソッドは、1 秒ごとに 1 つの SQL ステートメントを使用して、1 つのデータベース トランザクション内の数量を永続化します (たとえば)。

update x set q1 = q1 + delta1, q2 = q2 + delta2, ...

私はいくつかのテストを行いました:

  • 1000 メッセージを作成するのに 6 秒かかります (HornetQ を使用する JBoss 7)
  • その間、840 件のメッセージがすでに保持されていました。
  • 残りのメソッドを永続化するには、さらに 2 秒かかります (スケジュールされたメソッドは 1 秒ごとに実行されます)。
  • これには、7 つの DB トランザクションで 7 つの SQL 更新コマンドが必要でした
  • 負荷は完全にメッセージの作成によって発生します。DBに実際の負荷はありません

ノート

  • @PreDestroy保留中のデルタを永続化して、何も失われないようにする別の方法が必要です
  • トランザクションの正確性を保証する必要がある場合、このアプローチは適していません。その場合、通常のキュー レシーバー (= MDB なし)、トランザクション セッションを使用receive(timeout)し、100 ~ 10000 のメッセージを (またはタイムアウトまで) 収集し、1 つの DB トランザクションを実行し、その直後にキュー セッションでコミットすることをお勧めします。これは改善されましたが、まだ XA トランザクションではありません。これが必要な場合は、両方のコミットを 1 つの XA トランザクションで調整する必要があります。
于 2013-09-11T20:36:55.087 に答える