最近、私は非常に一般的であると想像していた問題に対処しなければなりませんでした: 処理する行数が多い (100 万以上) データベース テーブルがあり、さまざまなマシン/スレッドでさまざまなプロセッサが実行されている場合、各プロセッサ インスタンスを安全に許可する方法互いに干渉せずに大量の作業 (たとえば 100 項目) を取得するには?
一度にチャンクを取得する理由は、パフォーマンス上の理由からです。各アイテムのデータベースにアクセスしたくありません。
最近、私は非常に一般的であると想像していた問題に対処しなければなりませんでした: 処理する行数が多い (100 万以上) データベース テーブルがあり、さまざまなマシン/スレッドでさまざまなプロセッサが実行されている場合、各プロセッサ インスタンスを安全に許可する方法互いに干渉せずに大量の作業 (たとえば 100 項目) を取得するには?
一度にチャンクを取得する理由は、パフォーマンス上の理由からです。各アイテムのデータベースにアクセスしたくありません。
いくつかのアプローチがあります。各プロセッサにトークンを関連付け、そのトークンを次の [n] 個の利用可能なアイテムに対して設定する SPROC を用意することができます。おそらく次のようなもの:
(注 - 適切な分離レベルが必要です。おそらくシリアライズ可能: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
)
(TSQLを修正するために編集)
UPDATE TOP (1000) WORK
SET [Owner] = @processor, Expiry = @expiry
OUTPUT INSERTED.Id -- etc
WHERE [Owner] IS NULL
@expiry
プロセッサがダウンしても作業が失われないように、これにはタイムアウト ( ) も必要です。また、所有者の過去のものをクリアするタスクも必要ですExpiry
。
この質問と非常によく似ています:SQLServerプロセスキューの競合状態
クエリを実行して、特定のプロセッサIDに100行を割り当てます。これらのロックヒントを使用する場合、並行性の意味で「安全」です。そして、それはSETステートメントを必要としない単一のSQLステートメントです。
これは他の質問から取られています:
UPDATE TOP (100)
foo
SET
ProcessorID = @PROCID
FROM
OrderTable foo WITH (ROWLOCK, READPAST, UPDLOCK)
WHERE
ProcessorID = 0 --Or whatever unassigned is
コンシューマが処理中として作業を削除 (またはマーク) する特別なテーブルを作成したり、MSMQ や ActiveMQ などのミドルウェア キューイング ソリューションを使用したりできます。
ミドルウェアには独自の一連の問題があるため、可能であれば、特別なテーブルを使用します (可能な限り小さくし、できれば ID のみを使用して、ワーカーが残りの情報を自分でフェッチできるようにします)キュー テーブルを長時間ロックしないでください)。
このテーブルを定期的に埋めて、プロセッサが必要なものを上から取得できるようにします。
SQL テーブル キューに関する関連する質問:
プライオリティ キュー テーブルをクエリするための SQL の実行
ミドルウェアのキューイングに関する関連質問:
使用しているデータベース サーバーについては言及されていませんが、いくつかのオプションがあります。
INSERT
MySQL には、更新される行数を制限するための SQL99 の拡張機能が含まれています。各ワーカーに一意のトークンを割り当て、多数の行を更新してから、クエリを実行してそのワーカーのバッチを取得できます。マークはUPDATE TOP
構文を使用しましたが、データベース サーバーを指定しませんでした。
もう 1 つのオプションは、ロックに使用するテーブルを指定することです。読み取りのためにロックしたくないため、データと同じテーブルを使用しないでください。あなたのロックテーブルはおそらく 1 つの行だけを必要とし、次の ID には作業が必要です。ワーカーはテーブルをロックし、現在の ID を取得し、バッチ サイズに関係なく増分し、テーブルを更新してからロックを解除します。次に、データ テーブルにクエリを実行し、予約した行を取得できます。このオプションは、データ テーブルの ID が単調に増加することを前提としており、ワーカーが停止したり、バッチを完了できない場合は、フォールト トレラントではありません。