1

問題:

テーブルtable_aとが与えられた場合、が更新さtable_bれるたびに、次のような操作を確実に (かつ同時に) 実行する必要があります。table_a

  1. SELECTからのいくつかの行table_a
  2. アプリケーション コードで何かを計算します。
  3. UPDATEの行table_b

このようなことが起こるのを防ぐ必要があります (このシナリオ A と呼びますtable_b) table_a

  • worker1から行を取得しますtable_a
  • table_a更新されます。
  • worker2から行を取得しますtable_a
  • worker2更新しますtable_b
  • worker1更新しますtable_b

しかし、これは問題ありません (これをシナリオ B と呼びます) table_b

  • worker1から行を取得しますtable_a
  • table_a更新されます。
  • worker2から行を取得しますtable_a
  • worker1更新しますtable_b
  • worker2更新しますtable_b

取引:

REPEATABLE READ1 つの解決策は、すべてをトランザクションでラップすることです。次にworker1、 のトランザクションはシナリオ A で失敗し、worker2のトランザクションはシナリオ B で失敗します。シナリオを区別する方法がないため、失敗したトランザクションを再試行するしかありません。

しかし、どちらのシナリオでも無駄です。シナリオ A では、はすでに完全に更新されているworker1ため、 のトランザクションを再試行したくありません。シナリオ B では、正しいことを行っていたので、そもそも のトランザクションをtable_b失敗させたくありません。worker2

行のマーキング:

最初から行の主キーがわかっている場合table_b( :b_id)、各ワーカーに一意の ID がある場合 ( :worker_id)、別のことを試すことができます。markに列を追加し、table_bステップ 1 の前に各ワーカーにこれを実行させます。

UPDATE table_b SET mark = :worker_id WHERE id = :b_id;

WHERE次に、ステップ 3 で句を追加します。

UPDATE table_b SET ... WHERE ... AND mark = :worker_id;

必要worker1に応じて、両方のシナリオでステップ 3 の行を更新しません。

ここで行のマーキングは合理的なアプローチですか? 私が見逃している欠点は何ですか?この問題に対する「標準的な」解決策は何ですか?

明確化:私は PostgreSQL を使用しています。

4

1 に答える 1

2

テーブル b でトランザクションと SELECT ... FOR UPDATE を使用します。

これにより行ロックが発生し、ワーカー 1 がコミットするまでワーカー 2 がテーブル b を更新できなくなります。select for update でワークフローを開始すると、ワーカー 2 はワーカー 1 がコミットするまで開始できません。

2番目のアプローチ(おそらくより良い)は、選択を行うステートメントで更新をラップして、単一のステートメントにし、ロックを自動的に処理することです。

ワーカー 1 がコミットするまで、ワーカー 2 はマークされた行を表示しないため、行のマーキングはアイデアとして破棄する必要があります....

于 2012-09-11T12:29:22.260 に答える