2

PayPal即時支払い通知を受信したときに商品(配送保険証券など)を作成することになっているアプリケーションに取り組んでいます。残念ながら、PayPalは重複した通知を送信することがあります。さらに、PayPalから更新を取得するときにWebサービスの更新を同時に実行している別のサードパーティもあります。

これは、関連するデータベーステーブルの基本的な図です。

// table "package"
// columns packageID, policyID, other data...
// 
// table "insurancepolicy"
// columns policyID, coverageAmount, other data...

これが私がやりたいことの基本的な図です:

using (SqlConnection conn = new SqlConnection(...))
{
  sqlTransaction sqlTrans = conn.BeginTransaction(IsolationLevel.RepeatableRead);

  // Calls a stored procedure that checks if the foreign key in the transaction table has a value.
  if (PackageDB.HasInsurancePolicy(packageID, conn))
  { 
    sqlTrans.Commit();
    return false;
  }

  // Insert row in foreign table.
  int policyID = InsurancePolicyDB.Insert(coverageAmount, conn);
  if (policyID <= 0)
  {
    sqlTrans.Rollback();
    return false;
  }

  // Assign foreign key to parent table.  If this fails, roll back everything.
  bool assigned = PackageDB.AssignPolicyID(packageID, policyID, conn);
  if (!assigned)
  {
    sqlTrans.Rollback();
    return false;
  }
}

これを同時に実行する2つ(またはそれ以上)のスレッド(またはプロセスまたはアプリケーション)がある場合、ポリシーが作成されてpolicyIDが割り当てられるまで、最初のスレッドがpolicyIDを持たないときに「パッケージ」行をロックする必要がありますパッケージテーブルに。次に、policyIDがパッケージテーブルに割り当てられた後、ロックが解除されます。この同じコードを呼び出している他のスレッドがパッケージ行を読み取るときに一時停止して、最初にpolicyIDがないことを確認することを願っています。最初のトランザクションのロックが解除されたときに、2番目のトランザクションがpolicyIDが存在することを確認し、ポリシーテーブルに行を挿入せずに戻ることを期待しています。

注:CRUDデータベースの設計により、各ストアドプロシージャには、読み取り(選択)、作成(挿入)、または更新のいずれかが含まれていました。

これはRepeatableReadトランザクション分離の正しい使用法ですか?

ありがとう。

4

3 に答える 3

1

あなたは実際にシリアル化可能な分離レベルを望んでいると思います。問題は、2つのスレッドがHasInsurancePolicyCheckを通過する可能性があることです(InsurancePolicyDB.Insertが何をするのか、なぜ0を返すのかはわかりませんが)

これには他にも多くのオプションがあります。1つは、メッセージキューを使用して、これらの要求を自分でシリアルに処理することです。もう1つは、sp_getapplockを使用して、そのパッケージに固有のキーをロックすることです。そうすれば、必要以上の行やテーブルをロックする必要がなくなります。

于 2008-09-23T23:50:14.990 に答える
1

insert into Policy重複を挿入しようとしたときに一意性テーブルの制約にぶつかった方が安全でクリーンです。分離レベルを上げると、同時実行性が低下し、デッドロックなどの他の厄介な問題が発生する可能性があります。

もう1つの方法は、常にポリシー行を挿入し、パッケージがすでにポリシーに添付されている場合はそれをロールバックすることです。

begin tran (read committed)

/* tentatively insert new Policy */
insert Policy

/* attach Package to Policy if it's still free */
update Package
  set Package.policy_id = @policy_id
  where Package.package_id = @package_id and Package.policy_id is null

if @@rowcount > 0
  commit
else
  rollback

これは、競合がまれな場合に最適に機能します。これはあなたの場合のようです。

于 2008-09-24T00:40:42.077 に答える
0

aaronjensen の応答の「メッセージ キュー」のアイデアに同意します。複数の同時スレッドが同じ行のデータを同時に更新しようとしていることに懸念がある場合は、代わりにスレッドにデータをワーク キューに挿入させ、それを 1 つのスレッドで順次処理する必要があります。これにより、データベースの競合が大幅に減少します。これは、ターゲット テーブルが "N" ではなく 1 つのスレッドのみによって更新され、ワー​​ク キュー操作がメッセージング スレッドによる挿入と、データ処理スレッドによる読み取り/更新に制限されるためです。

于 2008-09-24T16:03:35.837 に答える