SELECT-UPDATEパターンを使用する場合の同時実行性の管理


25

次のコードがあるとしましょう(ひどいことは無視してください):

BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else

私の目には、これは並行性を適切に管理していません。トランザクションがあるからといって、更新ステートメントに到達する前に他の誰かがあなたと同じ値を読み取らないということにはなりません。

今、コードをそのままにしておきます(これは単一のステートメントとして処理するか、自動インクリメント/ ID列を使用することをお勧めします)並行性を適切に処理し、2つのクライアントが同じになる競合状態を防ぐ確実な方法id値?

WITH (UPDLOCK, HOLDLOCK)SELECTにa を追加するとうまくいくと確信しています。SERIALIZABLEトランザクション分離レベルは、(それは誰にもTRANが終わるまで、あなたが何をしたか読むことを拒否したことから、同様の作業に思えるでしょうUPDATE:これは偽であることがわかりマーティンの答え)。本当?どちらも同等に機能しますか?一方が他方よりも優先されますか?

IDの更新よりも正当なもの、つまり更新が必要な読み取りに基づいた計算を行うことを想像してください。多数のテーブルが関係している可能性がありますが、そのうちのいくつかは書き込みを行い、他のテーブルは書き込みを行いません。ここでのベストプラクティスは何ですか?

この質問を書いた後、必要なテーブルのみをロックしているので、ロックヒントの方が優れていると思いますが、誰かの入力に感謝します。

PSそして、いいえ、私は最良の答えを知りません、そして、本当に、より良い理解を得たいです!:)


明確にするために、2つのクライアントが同じ値を読み取ったり、update古いデータに基づいている可能性があるものを発行したりしないようにしますか?後者のrowversion場合、列を使用して、更新される行が読み取られてから変更されていないかどうかを確認できます。
a1ex07

最初のクライアントによって新しい値に更新される前に、2番目のクライアントが古いid値を取得するのは望ましくありません。ブロックするはずです。
エリック

回答:


11

SERIALIZABLE分離レベルの側面に対処するだけです。はい、これは機能しますが、デッドロックのリスクがあります。

2つのトランザクションは、両方とも同時に行を読み取ることができます。それらは、テーブル構造に応じてオブジェクトSロックまたはインデックスRangeS-Sロックを取得し、これらのロックは互換性があるため、お互いをブロックしません。ただし、更新に必要なIXロック(RangeS-Uそれぞれオブジェクトロックまたはインデックス)を取得しようとすると、それらは互いにブロックし、デッドロックを引き起こします。

UPDLOCK代わりに明示的なヒントを使用すると、読み取りがシリアル化され、デッドロックのリスクが回避されます。


1しかし:ヒープのテーブルのためにあなたはまだも、更新ロックで変換デッドロックを取得することができます。sqlblog.com/blogs/alexander_kuznetsov/archive/2009/03/11/...
AK

奇妙な、@ alex。実際にUPDLOCKする前にロックするものを見つけようとするエンジンの競合状態に関係していると思います
...-ErikE

@ErikE-Alexの記事の変換のデッドロックは、ヒープ自体の変換IXX行っています。興味深いことに、どの行も資格がないため、行ロックが解除されることはありません。なぜXロックを取得するのかまったくわかりません。
マーティンスミス

11

あなたにとって最善のアプローチは、実際にモジュールを高い並行性にさらし、自分自身で見ることだと思います。UPDLOCKだけで十分な場合があり、HOLDLOCKは不要です。sp_getapplockがうまく機能する場合があります。ここでは包括的なステートメントを作成しません。インデックス、トリガー、またはインデックス付きビューをもう1つ追加すると、結果が変わることがあります。テストコードを強調し、ケースバイケースで確認する必要があります。

ここでストレステストの例をいくつか書い

編集:内部のより良い知識のために、あなたはカレン・デラニーの本を読むことができます。ただし、他のドキュメントと同様に、書籍の同期がとれない場合があります。また、考慮すべき組み合わせが多すぎます:6つの分離レベル、多くの種類のロック、クラスター化/非クラスター化インデックス、他に何を知っているか。それは多くの組み合わせです。その上、SQL Serverはクローズドソースなので、ソースコードをダウンロードしたり、デバッグしたりすることはできません。これが究極の知識源となります。次のものは、次のリリースまたはサービスパックの後に不完全または古くなっている可能性があります。

したがって、独自のストレステストを行わずに、システムで何が機能するかを決定しないでください。何を読んだとしても、何が起こっているのかを理解するのに役立ちますが、読んだアドバイスがあなたに合っていることを証明する必要があります。誰もあなたのためにそれをできるとは思いません。


9

この特定のケースでは、UPDLOCKロックを追加すると、SELECT実際に異常が防止されます。添加はHOLDLOCK更新ロックは、トランザクションの期間中開催されるよう必要はありませんが、私は過去に(おそらく悪い)習慣としてそれを自分自身を含めへの告白します。

IDの更新、つまり更新が必要な読み取りに基づいた計算よりも正当なことを行うことを想像してください。多数のテーブルが関係している可能性がありますが、そのうちのいくつかは書き込みを行い、他のテーブルは書き込みを行いません。ここでのベストプラクティスは何ですか?

ベストプラクティスはありません。同時実行制御の選択は、アプリケーションの要件に基づいている必要があります。一部のアプリケーション/トランザクションは、データベースの排他的所有権があるかのように実行する必要があり、すべてのコストで異常や不正確さを回避します。他のアプリケーション/トランザクションは、互いにある程度の干渉を許容できます。

  • Webストア内の製品のバンド化された在庫レベル(<5、10 +、50 +、100 +)の取得=ダーティリード(不正確は関係ありません)。
  • そのWebストアでの在庫レベルの確認と削減=反復可能読み取り(販売する前に在庫がなければならず、負の在庫レベルになってはいけません)。
  • 銀行の当座預金口座と普通預金口座の間で現金を移動する=シリアル化可能(現金を誤って計算したり置き忘れたりしないでください!)。

編集:@AlexKuznetsovのコメントにより、質問を読み直し、回答の非常に明白なエラーを削除するよう促されました。深夜の投稿で自己に注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.