CREATE TABLEをロックする


19

別のアプリケーションでは、設計が悪いことに感銘を受けました。複数のスレッドがEnsureDatabaseSchemaExists()メソッドを同時に実行します。これは基本的に次のようになります。

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'MyTable') AND type = N'U') BEGIN

    CREATE TABLE MyTable ( ... );

END

ただし、SERIALIZABLEトランザクションで実行された場合でも、このコードはスレッドセーフではないようです(つまり、並列コードはテーブルを複数回作成しようとします)。別のスレッドがまったく同じSELECTステートメントを実行できないようにするロックを取得するためにSELECTステートメントを強制する機会はありますか?

multi-threaded-EnsureSchemaExists()メソッドのより良いパターンはありますか?

回答:


18

最善の策は、明示的な包含トランザクションを使用し、カスタム排他ロックを取得して、sp_getapplockを使用して操作全体(SELECTおよびCREATE TABLEを保護することです。システムオブジェクトは、設計上、分離レベルの要求を尊重せず、ユーザーテーブルと同じ方法でロックを使用します。

元のコードの競合状態は、複数のスレッドがテーブルが存在しないと結論付ける前に、スレッドがCREATE TABLEステートメントに到達することです。


6
+1は、applock がSELECTチェックをラップすることを確認するだけです。そうしないと、デッドロックが発生します。理想的には、Sモードでアプリをロックし、Xへのアップグレードを確認しますが、それはトリッキーです(控えめに言っても...)。最も安全なオプションは、Xを取得してから、DBスキーマ全体を展開することです。まれな操作(アプリ起動時など)である必要があるため、Xロックはそれほど重要ではありません。
レムスルサヌ14年

12

私の推奨事項は、ベストエフォートのtry / catchを行うことです。必要に応じて、重複するケースを明示的に処理します。それを無視します...

本当の質問:なぜDDLは複数のxactからオンデマンドで実行されるのですか?通常、アップグレードと移行は深刻な問題であり、専用の時間枠で処理されます...移行(コードファースト?)が予期せず開始されないように、これらの更新手順の一部は大きなテーブル(サイズの-データ操作...)


3
コードは、オンデマンドでテーブルを作成するDatabaseLoggerの一種です。移行も面白いビジネスもありません。しかし、あなたは完全に正しいです。コードを適切にリファクタリングします。
DR

4
また、昇格した特権コンテキスト(管理者など)で展開/セットアップを実行しても問題ありませんが、通常の操作はそうではないことを考慮してください。現在、あなたが必要としているCREATE TABLE...通常のオペレーションのための助成金を
レムスRusanu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.