2011-01-18 178 views
5

下表如何爲讀取操作 - 寫入操作設置鎖定?

Key(KeyId int, Sequence varchar(14))

考慮序列值是一個自定義自動增量鍵字母和數字組合特定的客戶端需要他的系統。

我們做了一個名爲GetNextSequence()的函數,它應該返回序列的下一個值。步驟來讀取和更新程序進行如下

  1. 閱讀使用keyid的序列:SELECT Sequence FROM [Key] WHERE KeyId = @Id
  2. 解析序列值,並確定下一個值
  3. 序列值寫入表: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; 

我們的問題駐留在兩個不同的服務器可以訪問相同的序列,我們最終得到死鎖

事務(進程ID X)被死鎖的鎖資源與另一個進程,並已被選作死鎖犧牲品。重新運行交易。

在C#中,我試圖建立不同的鎖組合就像使用表提示ROWLOCKHOLDLOCK事務隔離IsolationLevel.RepeatableReadIsolationLevel.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上的讀鎖。當A在其「客戶端」(在服務器程序內)工作時,進程B獲取讀卡器鎖。然後A在B在客戶端工作時請求升級到Write鎖,在此時它被告知要等到B的讀鎖被釋放。 B然後請求一個寫鎖定並等待,直到A釋放其Read。兩者現在都在等待,因此他們可以獲得更獨特的寫鎖。

解決方案是獨家鎖;你可以使用XLOCK提示來指定它。獨佔鎖基本上是爲讀取而獲取的寫入級別的鎖,並且在這種情況下用於您希望寫入您正在閱讀的內容的情況。正如註釋中所指出的那樣,只有在明確的交易範圍內執行該陳述時,纔會保留排他性鎖定,因此請確保在正在讀取該值的「工作單元」期間設置一個鎖定,確定如何前進它,然後更新它。

我會在行級使用這個(ROWLOCK),除非你一次更新很多類似的序列;獲取頁面或表級排他鎖使得EVERYBODY可以等待您不需要的數據,如果每個事務只處理一行。

+0

除非在顯式事務中,否則XLOCK僅保留當前語句。這本身還不夠。所以如果用於SELECT,你仍然可以在UPDATE上死鎖。 – gbn

+0

我假設一個明確的交易;函數GetNextSequence()從單詞go開始是一個原子「工作單元」。 – KeithS

+0

的確如此,但它應該更加清楚排他鎖的持久性 – gbn