2009-12-08 3 views
5

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); 

どのように改善できますか?例えば一定量(マジックナンバー)秒待つ。 より良い結果が得られる戦略はありますか?

注:デッドロックが実際にはほとんど発生しないように、いくつかのデータベースレベルのテクニックが使用されます。また、アプリケーションは、同じテーブルに同時に書き込むスレッドのスケジューリングを回避しようとします。上記の状況は単なる「最悪のシナリオ」に過ぎません。

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

答えて

10

よく使用されるアプローチは、何らかの形の指数バックオフです。 1000*attempts+random aproachではなく、遅延を試行回数の指数関数にします。これは最初の1回か2回の試行で待ち時間を最小限に抑えます。デッドロックが発生したのは不運だったかもしれませんが、接続が実際に輻輳していることが明らかである場合には、

もちろん、デッドロックが発生しにくいようにデータベースアクセスを調整する方法もあります。しかし、あなたのクエリが何をしているのか(そして実行された方法、実行された時)を知らなければ、それができるかどうかは言えません。

+0

指数関数は良いと思う - 私はそれをシミュレートしようとします! 私がこのノートで触れたように、アプリケーション設計は、データベースへのアクセスを好都合に配置することを含むデッドロックを回避することを目的としています。しかし、コーナーケースでは、多数のデッドロックが発生するという保証はありません。 – Adrian

+0

"指数関数的バックオフ"テクニックに関する技術的な参考資料はありますか? – Adrian

+2

これは、輻輳を回避するためにネットワークプロトコルで一般に使用されます。 wikiの記事をチェック:http://en.wikipedia.org/wiki/Exponential_backoff しかし、基本的な考え方は簡単です。何回かの指数関数を使用して、各再試行時の遅延を決定するだけです。正確な詳細は、あなたの目的に合わせて調整することができます。もちろん、最も簡単な実装は、2^n ms(ここでnはこれまでの再試行の回数)の間遅延することです。しかし、たぶんあなたは成長が遅すぎると思ったり、始まりが遅すぎたり、成長が遅すぎたりします。その後、乗算器を追加するか、または何かをnに追加します – jalf

1

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

私たちはランダムな遅延を混乱させませんでした。

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

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

ロックの競合(つまり、悲観的なテーブルレベルのロック)を回避すると、並行性が妨げられる傾向があります。ロックと競合しないトランザクションを定義できる場合は、デッドロックを避けることができます。ただし、同じテーブルへの同時アクセスは、デッドロックの定義とほぼ同じです。

読み込み時に、挿入(特にHEAPテーブル内)は多くの競合問題なしで並列処理を行うことができます。インデックスの作成を遅らせると、挿入中に他の更新は行われません。

したがって、組織をヒープに変更したり、複数の並行プロセス(またはスレッド、通常は複数のプロセスを持つ方が高速です)をロードしたり、インデックスを構築したりすることができますテーブル)では、デッドロックを回避することができます。

更新や削除を行うときはあまり役に立ちません。

+0

私のシミュレーション(20スレッド)では、ゼロ遅延によって膨大なデッドロックが発生します。大きな遅延は実際には経過時間全体を大幅に改善します。自動コミットはオンに設定されていますが、データベースのデッドロックには自動ロールバックも含まれます。 – Adrian

+0

@Adrian:かなり複雑なエラー処理が行われています。またCで作業していたので、それは「仮想例外」でした。最後に、コーディングを行っているC/Ingres n00bがいくつかありました。そのため、ロジックエラーがほとんど隠されていた場合に備えています。 –

+0

@Adrian:私たちの実際の練習では、遅れがないという存在は実用的な違いはありませんでした。負荷の高いOSは、それ自身のランダム化された遅延を導入します。 YMMV。私はあなたのシミュレーションを議論するつもりはありません。私たちがしたことをあなたに伝えています*。我々はランダムな遅れを混乱させなかった。 –

1

データベースに同時にアクセスする必要がない場合、単純な解決策として、データベースを削除し、タスク処理キューを使用してデータベースを更新し、キュー経由でデータベースへのアクセスをシリアル化することがあります。私はこれがあなたのアプリケーションに非同期要素を導入し、ほとんどのユーザ起動アプリケーションやオンラインWebアプリケーションには適していないが、バッチ/オフラインタイプのアプリケーションを検討する価値があるかもしれないことを認識している。しかし、)。

+0

処理キューはアプリケーションをかなり遅くしますが、デッドロックを回避するにはオプションではありません。プロダクションサーバーは4プロセッサーのUnixサーバーで、いくつかの並列スレッドを簡単に処理する必要があります – Adrian

0

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

+0

問題はありませんが、デッドロック/ロールバック/再試行が多すぎる場合のオプション –

+0

Oracleのような別のRDBMSへの移行は、このプロジェクトのオプションではありません。 シリアライズが試行され、デッドロックの数が少ない「通常の」実行よりもかなり遅くなりました – Adrian

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); 
関連する問題