2011-01-18 25 views
5

次の表読み取り/書き込み/書き込み操作のロックを設定するにはどうすればよいですか?

Key(KeyId int, Sequence varchar(14))

を考慮すると、シーケンス値が自分のシステムのための特定のクライアントのニーズその文字と数字を組み合わせた独自の自動インクリメントキーです。

シーケンスの次の値を返すGetNextSequence()という関数を作成しました。テーブルにシーケンス値を書くのシーケンス値を解析SELECT Sequence FROM [Key] WHERE KeyId = @Id

  • と次の値
  • を決定します:

    1. がKEYIDを使用してシーケンス値を読む従うとして読み込み、シーケンスを更新するステップが行くUPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Idここ

    (明確にするために簡略化)C#コードである:

    var transaction = connection.BeginTransaction(IsolationLevel.RepeatableRead); 
    var currentSequenceValue = SqlUtils.ExecuteScalar(connection, transaction, "SELECT Sequence FROM [Key] WHERE KeyId = @Id", new SqlParameter("@Id", keyId)); 
    var updatedSequenceValue = ParseSequence(currentSequenceValue); 
    SqlUtils.ExecuteScalar(connection, transaction, "UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id", new SqlParameter("@Id", keyId), new SqlParameter("@Sequence", updatedSequenceValue)); 
    transaction.Commit(); 
    return updatedSequenceValue; 
    

    私たちの問題は、同じ配列にアクセスできる2台の異なるサーバーに存在し、私たちはデッドロック

    トランザクション(プロセスID X)を取得し終わるのロックリソースを別のプロセスがデッドロックされ、デッドロックの対象として選択されています。トランザクションを再実行します。 C#ので

    は、私はテーブルヒントROWLOCKHOLDLOCKを使用してトランザクション分離IsolationLevel.RepeatableReadまたはIsolationLevel.SerializableまたはSQLのように異なるロックの組み合わせを設定しようとしたが、成功しませんでした。

    各サーバーは、アトミックな方法でシーケンスを読み取り、操作し、更新することができます。この状況でロックを設定する適切な方法は何ですか?

  • +0

    これはこの問題のようです:http://www.codinghorror.com/blog/2008/08/deadlocked.html – BrokenGlass

    答えて

    1

    トランザクションの期間(ROWLOCK、XLOCK、HOLDLOCK)に排他的な行レベルのロックを使用することをお勧めします。これまでのヒントなどは十分ではありません。

    BEGIN TRAN 
        SELECT Sequence FROM [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE KeyId = @Id 
    
        Parse the sequence value and determine the next value 
    
        UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id 
    COMMIT 
    

    が、私は、少なくとも一つのトランザクションに

    UPDATE [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK) 
        SET Sequence = dbo.scalarudf(...) 
        WHERE KeyId = @Id 
    

    編集をスコープを減らすことを見たい:あなたはSERIALIZABLE使用する場合はHOLDLOCKは必要ありません。範囲がロックされているために "RepeatableRead"が不十分かもしれません

    +0

    ああ、私は "解析シーケンス"はC#で作成されていてSQL Serverで。 –

    +1

    @ Pierre-Alain Vigeant:あなたはまだヒントと取引が必要です。 C#をCLR関数またはSQLに移動できますか? – gbn

    +0

    それは 'ROWLOCK、XLOCK、HOLDLOCK'で動作するようですが、私は明日何かを行うつもりのテストが必要です。 –

    2

    問題は、同じレコードで複数の読み取りロックを取得できるため、読み取りのために取得されるデフォルトのロックが競合状態を回避しないことです。

    状況Aは、行Xの読み取りロックを取得します。プロセスBは、Aが「クライアント側」(サーバープログラム内)で作業しているときに読み取りロックを取得します。 Aは、Bがクライアント側で作業している間に書き込みロックへのアップグレードを要求します。この時点で、Bの読み取りロックが解除されるまで待機するように指示されます。 BはWriteロックを要求し、AがReadを解放するまで待つ。両方がもう一方を待っているので、より排他的な書き込みロックを取得できます。

    解決策は排他ロックです。 XLOCKヒントを使用してこれを指定できます。排他ロックは、基本的には読み込みのために取得された書き込みレベルのロックであり、読んでいるものを書き込むことが予想されるこのまさにそのケースで使用されます。コメントに記載されているように、排他ロックは、明示的なトランザクションのスコープ内でステートメントが実行される場合にのみ維持されるため、値を読み取っている「作業単位」の中で1つを設定し、それを更新します。

    多くの類似したシーケンスを一度に更新しない限り、これを行レベル(ROWLOCK)で使用します。ページまたはテーブルレベルの排他ロックを取得すると、トランザクションごとに1つの行だけを処理している場合には、必要のないデータをEVERYBODYが待つことになります。

    +0

    XLOCKは、明示的なトランザクションでない限り、現在のステートメントに対してのみ存続します。それだけでは十分ではありません。 SELECTに使用すると、UPDATEでデッドロックすることができます。 – gbn

    +0

    私は明示的な取引を想定していました。関数GetNextSequence()は、単語goからアトミックな「作業単位」です。 – KeithS

    +0

    Trueですが、排他ロックの永続性についてはより明確になるはずです – gbn

    関連する問題