2012-11-17 33 views
5

我正在編寫一個過程,將協調實時數據庫上的finical交易。我正在做的工作不能作爲集合操作完成,所以我使用了兩個嵌套遊標。正確的方式採取獨佔鎖

我需要一個排它鎖的事務表,而我每個客戶端的協調,但我想解除鎖定,並讓其他人在每一個客戶我處理之間運行他們的查詢。

我很想做一個行級別而不是表級別一排它鎖,但what I have read so far說,如果其他事務在READCOMMITED隔離級別(這是對我來說)運行,我不能做with (XLOCK, ROWLOCK, HOLDLOCK)

我是否正確地採取了表級排它鎖,並且有在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 
+0

我想弄明白爲什麼你需要獨佔鎖。其他人可能會插入記錄嗎?其他人更新記錄?你擔心其他人對數據有不一致的看法嗎? – Laurence

+0

@Laurence我很擔心其他人會看到不一致的狀態。我試圖糾正一個錯誤,影響了一小部分的客戶端,但是這個糾正過程在幾個表中留下了幾個相互依賴的行(我實際上會鎖定5個表,但我的代碼示例簡化爲一個表)處於不一致狀態校正過程。每個客戶端的不一致性是孤立的,但是在這個客戶端上執行'SELECT SUM(ColA)FROM trnHistory'會在「更正」過程中返回一個不正確的值。所以我需要採取排他鎖來防止讀取。 –

+0

我不明白爲什麼交易不能保護你免受這種情況的影響,除非你有人在做read_uncommitted。 – Laurence

回答

3

如果你只擔心其他讀者,那麼你不應該需要獨佔鎖,模式

Begin Transaction 

    Make Data Inconsistent 

    Make Data Consistent 

Commit Transaction 

應該罰款。唯一會看到不一致數據的會話是那些使用nolockRead Uncommitted的會話,或那些希望在不使用Repeatable RowsSerializable的情況下進行多個一致讀取的會話。

在回答這個問題時,在我看來,採取排他鎖定的正確方法是安排事情,讓引擎爲你做。

9

我簡直不敢相信,一個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,也有一種情況是一個事務可以專門鎖定一行,而另一個事務可以讀取同一行!這可能在索引存在的情況下發生。一個事務可能在非聚集索引IX_T_SomeCol上X鎖定,而另一個事務則快樂地讀取聚集索引PK_T

所以它實際上是相當正常的交易,即使在獨佔鎖定的情況下獨立執行。

+2

我發現只有在讀取sql塊時使用REPEATABLE READ或SERIALIZABLE隔離級別(如果uncommited rowlock + xlock存在) – SalientBrain

+1

是的,只有隔離級別REPEATABLE READ或SERIALIZABLE可能會阻塞會話2,因爲即使會話獲得了XLOCK ,即使你沒有提交事務,會話1在讀完之後已經釋放了XLOCK。它不再持有它。 – Xin

+0

@Xin由於HOLDLOCK,它確實保留了它。這與'SERIALIZABLE'完全相同,並保存所有鎖直到事務結束。 – usr