6

ライブデータベースで最終的なトランザクションを調整する手順を書いています。私が行っている作業は集合演算として実行できないため、2つのネストされたカーソルを使用しています。

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

テーブルレベルではなく行レベルで排他ロックを実行したいのですが、これまで読んだことにwith (XLOCK, ROWLOCK, HOLDLOCK)よると、他のトランザクションが分離レベルで実行されている場合は実行できませんREADCOMMITED(これは私にとってです)。

テーブルレベルの排他ロックを正しく取得していますか?Server 2008 R2で、データベースで実行されている他のクエリを変更せずに、行レベルの排他ロックを希望どおりに機能させる方法はありますか?

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
4

2 に答える 2

14

XLOCKで同時リーダーをブロックしないとは信じられなかったread 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)

私はこれが機能することをテストしました。


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

したがって、排他的ロックが存在する場合でも、トランザクションが独立して実行できるのは実際にはごく普通のことです。

于 2012-11-18T00:55:19.927 に答える
5

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

Begin Transaction

  Make Data Inconsistent

  Make Data Consistent

Commit Transaction

大丈夫なはずです。一貫性のないデータが表示されるセッションは、またはを使用するセッション、またはnolockまたはRead Uncommittedを使用せずに複数の一貫性のある読み取りを行うことを期待するセッションのみです。Repeatable RowsSerializable

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

于 2012-11-18T12:15:20.010 に答える