1

SQL Server データベース テーブルをワーク キューとして使用しています。

1 人のライターと複数のリーダーがいます。

ライターは、キュー テーブルの新しいレコードを作成します (INSERT)。

各リーダーは、使用する特定のステータス (SELECT) のレコードを探し、レコードの 1 つのバッチを取得し、各レコードを所有済みとしてマークし (UPDATE)、それらを処理してから、処理されたすべてのレコードを完了または失敗としてマークします (UPDATE)。レコードのワークフローは単純です。ステータスは、追加の「A」から処理中の「B」、完了の「C」または失敗の「F」のいずれかに変化します。そして、各レコードの所有者は、ステータスが「A」のときの未設定から、残りのステップのリーダーを識別する一意の識別子になります。一意の識別子 (Decimal)、ステータス (nchar(1))、ライターのより大きなバッチを識別する追加のバッチ ID (int) が、処理における 3 つの重要なフィールドです。また、テーブルには、LINQ が同時実行チェックに使用するタイムスタンプ フィールドがあります。

2 人のリーダーが処理する同じレコードを選択することによるロックの遅延と更新の失敗を防ぐ必要があります。処理作業の一環として、完了までに不確定な時間がかかる Web サービス呼び出しが行われます。私たちは Web サービス ベンダーにトランザクションで支払うので、呼び出しを行いたくないので、同じレコードを処理する別のプロセスを見つけます。このようなエラーは、数千ドルの費用がかかります。

私はこの記事を読みました:

READPAST および UPDLOCK を使用した SQL Server でのデータ キューの処理

それは有望に見えましたが、LINQ to SQL を使用することにしました。私はこの記事を読みました:

UPDLOCK を使用した Linq to SQL

後者は、SQL プロシージャを大まかにラップする以外に、UPDLOCK を使用するように LINQ に指示する方法はないと述べています。これは、LINQ が楽観的同時実行を使用し、これらの機能が悲観的ロックを想定しているためです。

では、LINQ がサポートする機能を使用してこの問題を解決するにはどうすればよいでしょうか? 私のアプリケーションはマルチスレッドですが、同じキュー テーブルに対して複数のインスタンスを実行できます。そのため、1 つのディスパッチャだけで各コンシューマ スレッドにレコードを渡すことはできません。

.NET 4.0 を使用しています。私のアプリケーションは、単純な古い Windows コンソール アプリケーションです。このソリューションは、SQL Server 2005 および SQL Server 2008 R2 で動作するはずです。メッセージング システムを使用する必要はありません。

更新:SOに関するさらなる研究。この記事を見つけました:

LINQ to SQL - キュー

答えは、私が除外したディスパッチャ スレッドを持つことを推奨しています。

更新 2:SO に関する別の記事が見つかりました:

データベース内の行のグループを原子的にマークして返す

これは本当に有望に見えます。ダイレクト SQL を実行する必要がありますが、レコードにマークを付けて ID を取得したら、残りは L2S で行うことができます。

4

2 に答える 2

0

おそらく、リーダー内のデキュープロセスに対してより悲観的なアプローチを取ることができます。

リーダーに次のキューに入れられたアイテムを受け取らせます。次に、リーダーにそのアイテムのデキューを試みてもらいます。リーダーは、GUIDなどのデキュー呼び出しの項目に一意の値を割り当てることができます。

次に、リーダーは、キューから取り出されたキューに入れられたアイテムの取得を試みることができます。アイテムを受け取ると、割り当てたアイテムのGUIDを比較できます。それらが同じである場合、リーダーが処理できるのはデキューでした。

リーダーが一度に1つのアイテムをデキューすることが可能であると仮定すると、これは実行可能であるはずです。リーダーごとに複数のアイテムをデキューするときに、それがどのように機能するかわかりません。

于 2012-11-09T13:46:05.157 に答える
0

serveこれが私の答えです。アドレスを予約するために、ストアド プロシージャを DataContext に関連付けました。ペシミスティックな同時実行性を使用して、1 回の操作でレコードを選択、マーク、および返すことができます。

CREATE PROCEDURE dbo.kccsp_ReserveAddresses
      @BATCH_SIZE int,
      @BATCH_ID int,
      @PROCESS_TOKEN numeric(18,0)
  AS
      -- Reserve a group of addresses in the queue so that a consumer may process them,
      -- but all other consumers will leave them alone.
      -- The query hints are essential to prevent lock contention, 
      -- or concurrency errors from multiple processors handling the same address.
      update TOP (@BATCH_SIZE) 
          KCC_GeoCodingAddressQueue WITH (ROWLOCK, READPAST, UPDLOCK)
      set 
          [Process Status] = 'B', 
          [Process Token] = @PROCESS_TOKEN
      output INSERTED.*
      where [Process Status] = 'A' and [Process Token] = 0

これは、dbml のデザイナでプロシージャを結果テーブルにドラッグすることで行いました。これにより、結果セットがエンティティ クラスと同じになり、割り当てられたすべてのレコードがマークと同時に返されます。SQL ヒントは、並行性とロックの競合を処理します。

C# で呼び出す必要がある場合は、次のようにします。

   int? batchSize = 50;
   int? batchIdToReserve = Batch.GeoBatchID;
   decimal? processorId = someId;
   var addresses = Context.kccsp_ReserveAddresses(batchSize, batchIdToReserve, processorId);
于 2012-11-09T17:11:35.947 に答える