我們有一個最初作爲桌面應用程序編寫的應用程序,這些應用程序在很多年前都是這樣。無論何時打開編輯屏幕,它都會啓動事務,並在您單擊確定時提交;如果單擊取消,則會回滾。這對於桌面應用程序來說工作得很好,但現在我們正在嘗試轉向ADO.NET和SQL Server,並且長時間運行的事務是有問題的。我怎樣才能讓SQL Server事務使用記錄級鎖?
我發現當多個用戶都試圖同時編輯同一個表(不同的子集)時,我們會遇到問題。在我們的舊數據庫中,每個用戶的事務將獲取記錄級鎖,以鎖定他們在事務中修改的每條記錄;因爲不同的用戶正在編輯不同的記錄,每個人都有自己的鎖,一切正常。但是在SQL Server中,只要有一個用戶在一個事務中編輯了一條記錄,SQL Server似乎就會在整個表上獲得一個鎖。當第二個用戶試圖編輯同一個表中的另一個記錄時,第二個用戶的應用程序會簡單地鎖定,因爲SqlConnection會阻塞,直到第一個用戶提交或回滾。
我知道長時間運行的交易很糟糕,而且我知道最好的解決方案是更改這些屏幕,以便它們不再讓交易長時間保持打開狀態。但是,由於這意味着一些侵入性和風險性的變化,我還想研究是否有辦法讓代碼正常運行,只是讓我知道我的選擇是什麼。
如何在SQL Server中獲取兩個不同用戶的事務來鎖定單個記錄而不是整個表?
這裏是一個快速和骯髒的控制檯應用程序,說明問題。我創建了一個名爲「test1」的數據庫,其中一個名爲「Values」的表具有ID(int)和Value(nvarchar)列。如果您運行該應用程序,它會要求修改一個ID,啓動一個事務,修改該記錄,然後保持事務處於打開狀態,直到您按下ENTER鍵。我希望能夠
- 啓動程序並告訴它更新ID 1;
- 讓它得到它的交易並修改記錄;
- 啓動程序的第二個副本,並告訴它更新ID 2;
- 它能夠在第一個應用程序的事務處於打開狀態時更新(並提交)。
目前它凍結在第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語句上
原來我沒有;我使ID成爲自動編號字段,但我沒有將其作爲主鍵。當我添加主鍵時,它獲得了記錄級鎖。我不知道爲什麼主鍵應該有所作爲,但顯然它有。謝謝! – 2010-04-16 14:23:17
不客氣。我確信有更多的DBA說這種方式,但是AIUI,這意味着你在查找目標記錄時避免了表掃描,這就是鎖定表的原因。 – 2010-04-16 16:33:54