3

我們有一個最初作爲桌面應用程序編寫的應用程序,這些應用程序在很多年前都是這樣。無論何時打開編輯屏幕,它都會啓動事務,並在您單擊確定時提交;如果單擊取消,則會回滾。這對於桌面應用程序來說工作得很好,但現在我們正在嘗試轉向ADO.NET和SQL Server,並且長時間運行的事務是有問題的。我怎樣才能讓SQL Server事務使用記錄級鎖?

我發現當多個用戶都試圖同時編輯同一個表(不同的子集)時,我們會遇到問題。在我們的舊數據庫中,每個用戶的事務將獲取記錄級鎖,以鎖定他們在事務中修改的每條記錄;因爲不同的用戶正在編輯不同的記錄,每個人都有自己的鎖,一切正常。但是在SQL Server中,只要有一個用戶在一個事務中編輯了一條記錄,SQL Server似乎就會在整個表上獲得一個鎖。當第二個用戶試圖編輯同一個表中的另一個記錄時,第二個用戶的應用程序會簡單地鎖定,因爲SqlConnection會阻塞,直到第一個用戶提交或回滾。

我知道長時間運行的交易很糟糕,而且我知道最好的解決方案是更改這些屏幕,以便它們不再讓交易長時間保持打開狀態。但是,由於這意味着一些侵入性和風險性的變化,我還想研究是否有辦法讓代碼正常運行,只是讓我知道我的選擇是什麼。

如何在SQL Server中獲取兩個不同用戶的事務來鎖定單個記錄而不是整個表?

這裏是一個快速和骯髒的控制檯應用程序,說明問題。我創建了一個名爲「test1」的數據庫,其中一個名爲「Values」的表具有ID(int)和Value(nvarchar)列。如果您運行該應用程序,它會要求修改一個ID,啓動一個事務,修改該記錄,然後保持事務處於打開狀態,直到您按下ENTER鍵。我希望能夠

  1. 啓動程序並告訴它更新ID 1;
  2. 讓它得到它的交易並修改記錄;
  3. 啓動程序的第二個副本,並告訴它更新ID 2;
  4. 它能夠在第一個應用程序的事務處於打開狀態時更新(並提交)。

目前它凍結在第4步,直到我回到應用程序的第一個副本並關閉它或按ENTER鍵,它提交。對command.ExecuteNonQuery的調用會阻塞,直到第一個連接關閉。

public static void Main() 
{ 
    Console.Write("ID to update: "); 
    var id = int.Parse(Console.ReadLine()); 
    Console.WriteLine("Starting transaction"); 
    using (var scope = new TransactionScope()) 
    using (var connection = new SqlConnection(@"Data Source=localhost\sqlexpress;Initial Catalog=test1;Integrated Security=True")) 
    { 
     connection.Open(); 
     var command = connection.CreateCommand(); 
     command.CommandText = "UPDATE [Values] SET Value = 'Value' WHERE ID = " + id; 
     Console.WriteLine("Updating record"); 
     command.ExecuteNonQuery(); 
     Console.Write("Press ENTER to end transaction: "); 
     Console.ReadLine(); 
     scope.Complete(); 
    } 
} 

這裏有一些事情我已經試過了,在行爲沒有變化:

  • 更改事務隔離級別爲 「讀未提交」
  • 指定 「WITH(ROWLOCK)」在UPDATE語句上

回答

5

只是檢查,但你對ID列主鍵或唯一索引?

+0

原來我沒有;我使ID成爲自動編號字段,但我沒有將其作爲主鍵。當我添加主鍵時,它獲得了記錄級鎖。我不知道爲什麼主鍵應該有所作爲,但顯然它有。謝謝! – 2010-04-16 14:23:17

+1

不客氣。我確信有更多的DBA說這種方式,但是AIUI,這意味着你在查找目標記錄時避免了表掃描,這就是鎖定表的原因。 – 2010-04-16 16:33:54

1

調查樂觀與悲觀鎖定。

編輯: 上一篇文章鏈接到經典ado ...對不起。

http://msdn.microsoft.com/en-us/library/cs6hb8k4(VS.71).aspx

+0

那頁說「如果你的基礎數據源支持事務,你可以在一個事務中更新的數據來模擬悲觀併發。」所以看起來它對於樂觀併發的討論僅適用於*不*使用事務的情況。 – 2010-04-15 16:00:10

+0

這是幫助嗎? http://articles.techrepublic.com.com/5100-10878_11-1049842.html 它討論了使用「可重複讀」選項集創建事務本身。 – Jeremy 2010-04-15 16:12:50

+0

還可以查看 http://www.sql-server-performance.com/articles/per/new_isolation_levels_p1.aspx 瞭解SQL 2005及更高版本中可用的不同隔離級別。 我相信sql 2000和2005之間的默認隔離級別發生了變化,所以當服務器忙的時候,從一個升級到另一個的應用程序有一些令人討厭的驚喜。 – Jeremy 2010-04-15 16:20:55

1

可能索引是在行鎖被設置爲「off」的情況下創建的。
查詢中的「WITH(ROWLOCK)」在這種情況下不起作用。

您可以ALTER INDEX它們重新打開,例如:

ALTER INDEX [PK_Values] ON [Values] SET (ALLOW_ROW_LOCKS = ON)