2012-11-17 9 views
5

ライブデータベース上の精巧なトランザクションを調停する手順を書いています。私が行っている作業はセット操作として行うことができないので、2つのネストされたカーソルを使用しています。排他的ロックを取る正しい方法

私はクライアントごとに調整している間にトランザクションテーブルを排他的にロックする必要がありますが、ロックを解除して、処理する各クライアントの間で他の人にクエリを実行させたいと思います。

私の代わりに、テーブルレベルの行レベルの排他ロックを行うのが大好きだが、what I have read so farは、他のトランザクションが(それは私のためである)READCOMMITED分離レベルで実行されている場合、私はwith (XLOCK, ROWLOCK, HOLDLOCK)を行うことができないと言います。

テーブルレベルの排他ロックを正しく実行していますが、行レベル排他ロックをデータベースで実行中の他のクエリを変更せずに動作させる方法はありますか?

declare client_cursor cursor local forward_only for 
    select distinct CLIENT_GUID from trnHistory 
open client_cursor 

declare @ClientGuid uniqueidentifier 
declare @TransGuid uniqueidentifier 

fetch next from client_cursor into @ClientGuid 
WHILE (@@FETCH_STATUS <> -1) 
BEGIN 
    IF (@@FETCH_STATUS <> -2) 
    BEGIN 
     begin tran 

     declare @temp int 

     --The following row will not work if the other connections are running READCOMMITED isolation level 
     --select @temp = 1 
    --from trnHistory with (XLOCK, ROWLOCK, HOLDLOCK) 
    --left join trnCB with (XLOCK, ROWLOCK, HOLDLOCK) on trnHistory.TRANS_GUID = trnCB.TRANS_GUID 
    --left join trnClients with (XLOCK, ROWLOCK, HOLDLOCK) on trnHistory.TRANS_GUID = trnClients.TRANS_GUID 
    --(Snip) --Other tables that will be "touched" during the reconcile 
    --where trnHistory.CLIENT_GUID = @ClientGuid 

     --Works allways but locks whole table. 
    select top 1 @temp = 1 from trnHistory with (XLOCK, TABLOCK) 
    select top 1 @temp = 1 from trnCB with (XLOCK, TABLOCK) 
    select top 1 @temp = 1 from trnClients with (XLOCK, TABLOCK) 
    --(Snip) --Other tables that will be "touched" during the reconcile 

     declare trans_cursor cursor local forward_only for 
       select TRANS_GUID from trnHistory where CLIENT_GUID = @ClientGuid order by TRANS_NUMBER 
     open trans_cursor 

     fetch next from trans_cursor into @TransGuid 
     WHILE (@@FETCH_STATUS <> -1) 
     BEGIN 
      IF (@@FETCH_STATUS <> -2) 
      BEGIN 

       --Do Work here 

      END 
      fetch next from trans_cursor into @TransGuid 
     END 

     close trans_cursor 
     deallocate trans_cursor 

      --commit the transaction and release the lock, this allows other 
      -- connections to get a few queries in while it is safe to read. 
     commit tran 
    END 

    fetch next from client_cursor into @ClientGuid 
END 

close client_cursor 
deallocate client_cursor 
+0

私は排他ロックが必要な理由を理解しようとしています。他の人がレコードを挿入しそうですか?レコードを更新する他の人?あなたは、他の人々がデータの一貫性のない見方をすることを心配していますか? – Laurence

+0

@Laurence私は、他の人が一貫性のないビュー状態になることを心配しています。私はクライアントの小さな%に影響を与えるエラーを修正しようとしていますが、訂正プロセスはいくつかの相互依存する行をいくつかのテーブルに残します(実際には5つのテーブルをロックしますが、補正処理を行う。矛盾はクライアントごとに分離されますが、その1つのクライアントで 'SELECT SUM(ColA)FROM trnHistory'を実行すると、「訂正」プロセス中に不正な値が返されます。だから私は読み込みを防ぐために排他的なロックを取る必要があります。 –

+0

私はread_uncommittedをしている人がいない限り、トランザクションがあなたをこのことから保護しない理由を理解していません。 – Laurence

答えて

3

あなただけの他の読者を心配している場合は、排他ロックを必要はありません、パターン

Begin Transaction 

    Make Data Inconsistent 

    Make Data Consistent 

Commit Transaction 

は問題ないはずです。一貫性のないデータが表示される唯一のセッションは、nolockまたはRead Uncommittedを使用するセッション、またはRepeatable RowsまたはSerializableを使用せずに複数の整合性のある読み取りを行うセッションです。

質問に答えて、排他的なロックを取る正しい方法は、私の意見では、エンジンがあなたのために行うように整理することです。

9

私はので、私はそれを再現XLOCKread committedで同時リーダーをブロックしないだろうと信じることができませんでした:それは本当です。スクリプト:

セッション1:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
BEGIN TRAN 

SELECT * FROM T WITH (ROWLOCK, XLOCK, HOLDLOCK /*PAGLOCK, TABLOCKX*/) WHERE ID = 123 

セッション2:あなたは手元に持っているいくつかのテーブル名で

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
BEGIN TRAN 

SELECT * FROM T WHERE ID = 123 

プラグ。セッション2はブロックされていません。

PAGLOCKでも試してみましたが、うまくいきませんでした。次に私はTABLOCKXを試しましたが、どちらもうまくいきませんでした!

したがって、テーブルロックベースの戦略は機能しません。私はあなたが作家によってブロックされるように、より高い分離レベルを使用して、彼らはどちらか

  1. 使用スナップショット分離を(任意の書き込み前のような)一貫性のあるビューを取得するための
  2. ように読者を変更する必要が思う

もちろん、実際にテーブルをロックするための厄介な回避策があります。スキーマを変更します。これは基本的にテーブルへのアクセスと競合するSch-Mロックをとります。それはいくつかのメタデータ読み取り操作を保持します。これは次のようになります。

--just change *any* setting in an idempotent way 
ALTER TABLE T SET (LOCK_ESCALATION = AUTO) 

私はこれを動作させるためにテストしました。


XLOCKに従っていないSQL Serverはありますか?それとも、これは製品の欠陥ですか?私はそれがREAD COMMITTEDの文書化された特性に従っているのでそれが正しいと思います。また、SERIALIZABLEを使用しても、1つのトランザクションで排他的に行をロックし、別のトランザクションで同じ行を読み取ることができる場合があります。これは、インデックスが存在する場合に発生する可能性があります。 1つのトランザクションは非クラスタ化インデックスIX_T_SomeColをXロックし、別のトランザクションはクラスタ化インデックスPK_Tを読み込みます。

したがって、排他的ロックが存在する場合でもトランザクションが独立して実行できるということは、実際はかなり正常です。

+2

私はそれが見つかった場合のみ、sqlブロックを読み込み中にREPEATABLE READまたはSERIALIZABLE分離レベルを使用している場合(コミットされていないrowlock + xlockが存在する場合) – SalientBrain

+1

REPEATABLE READまたはSERIALIZABLEの分離レベルはセッションがブロック2をブロックする可能性があります。また、トランザクションをコミットしていないにもかかわらず、セッション1は、読み込みが完了した後にXLOCKを解放しています。それはもはやそれを保持していませんでした。 – Xin

+0

@Xinそれは 'HOLDLOCK'のためにそれを保持しました。これは 'SERIALIZABLE'と同じで、トランザクションが終了するまですべてのロックを保持します。 – usr

関連する問題