6

Java 6 アプリケーション内からデータベースのデッドロックを処理するための優れた戦略を探しています。複数の並列スレッドが同時に同じテーブルに書き込む可能性があります。データベース (Ingres RDMBS) は、デッドロックを検出すると、セッションの 1 つをランダムに強制終了します。

次の要件が与えられた場合、デッドロック状況に対処するための受け入れ可能な手法は何でしょうか?

  • 合計経過時間は、合理的に可能な限り短く保つ必要があります
  • セッションを強制終了すると、重大な (測定可能な) ロールバックが発生します
  • スレッドが
    互いに通信する方法がない場合、つまり、戦略は自律的でなければなりません

これまでのところ、私が思いついた戦略は次のようなものです。

short attempts = 0;
boolean success = false;
long delayMs = 0;

Random random = new Random();
do {
    try {
        //insert loads of records in table 'x'
        success = true;
    } catch (ConcurrencyFailureException e) {
        attempts++;
        success = false;
        delayMs = 1000*attempts+random.nextInt(1000*attempts);

        try {
                Thread.sleep(delayMs);
            } catch (InterruptedException ie) {
        }
    }
} while (!success);

何らかの方法で改善できますか?たとえば、一定量 (マジック数) 秒待機します。より良い結果を生み出す別の戦略はありますか?

注:デッドロックが実際には非常にまれであることを確認するために、いくつかのデータベース レベルの手法が使用されます。また、アプリケーションは、同時に同じテーブルに書き込むスレッドをスケジュールすることを回避しようとします。上記の状況は、あくまでも「最悪のシナリオ」になります。

注:レコードが挿入されるテーブルは、ヒープ パーティション テーブルとして編成され、インデックスはありません。各スレッドは、独自のパーティションにレコードを挿入します。

4

5 に答える 5

11

一般的に使用されるアプローチは、ある種の指数バックオフです。アプローチではなく1000*attempts+random、遅延を試行回数の指数関数にします。これにより、最初の1〜2回の試行で遅延が最小限に抑えられます。この場合、デッドロックしたのは運が悪かったかもしれませんが、接続が実際に混雑していることが明らかな場合は、後ではるかに大きな遅延が発生します。

もちろん、別のアプローチは、デッドロックが発生する可能性が低くなるようにデータベースアクセスを調整することです。しかし、クエリが何をするのか(そして、どのように、いつ実行されるのか)を知らなければ、それができるかどうかを言うことは不可能です。

于 2009-12-08T11:19:07.820 に答える
1

それが私たちのやり方です。トランザクションをループして、完了するまで再試行します。

ランダムな遅延を台無しにしませんでした。

また、tryブロック内でコミットを実行し、例外ハンドラーでロールバックを実行しました。

複数のロック可能なリソースと複数の同時トランザクションがある場合、デッドロックは避けられません。これは、ロックの競合の論理的帰結です。

ロックの競合(つまり、悲観的なテーブルレベルのロック)を回避すると、同時実行も防止される傾向があります。ロックを争わないトランザクションを定義できれば、デッドロックを回避できます。ただし、同じテーブルへの同時アクセスは、ほとんどデッドロックの定義です。

ロード時に、挿入(特にHEAPテーブルの場合)は、多くの競合の問題なしに(多くの場合)並行して続行できます。インデックスの作成を遅らせると、挿入中に他の更新は行われません。

したがって、インデックスを削除し、組織をヒープに変更し、複数の同時プロセス(またはスレッド、通常は複数のプロセスを持つ方が高速です)をロードしてから、インデックスを構築する(場合によってはテーブルを再編成する)ことで回避できる場合があります。デッドロックを回避できる場合があります。

更新または削除を行う場合、あまり役に立ちません。

于 2009-12-08T11:17:39.383 に答える
1

データベースへの同時アクセスが必要ない場合、簡単な解決策は、データベースを削除し、代わりにタスク処理キューを使用してデータベースを更新し、キューを介してデータベースへのアクセスをシリアル化することです。これによりアプリケーションに非同期要素が導入されるため、ほとんどのユーザー主導のアプリケーションやオンラインWebアプリには適していませんが、バッチ/オフラインタイプのアプリケーションを検討する価値があるかもしれません(おそらくあなたの見た目の答えではないことを理解しています)ただし)。

于 2009-12-08T11:18:10.443 に答える
0

Ingresのようなデータベースでは、常にデッドロックが発生するため、挿入、更新、または削除が失敗し、再試行戦略が実施されていることを想定する必要があります(例のように)。競合が最小限に抑えられ、デッドロックがほとんど発生しないようにデータベースを設計する必要があります。何度か再試行してもトランザクションが失敗し続ける場合は、データベースを大幅に再設計する必要があることを示しています(または、適切な使用によってデッドロックを回避するようにアプリケーションを設計できるOracleなどのシステムに移行する必要があります)。行レベルのロックの)。

于 2009-12-08T11:19:36.023 に答える
0

これはどのように ?

short attempts = 0;
boolean success = false;
long delayMs = 0;

Random random = new Random();
do {
try {
     synchronized(ClassName.class) {
         //insert loads of records in table 'x'
      }

    success = true;
} catch (ConcurrencyFailureException e) {
    attempts++;
    success = false;
    delayMs = 1000*attempts+random.nextInt(1000*attempts);

    try {
                    Thread.sleep(delayMs);
            } catch (InterruptedException ie) {
    }
  }
} while (!success);
于 2009-12-08T11:22:33.547 に答える