我在SQL Server 2008上的SELECT/UPDATE上遇到了死鎖問題。 我從這個線程讀取了答案:但我仍然不明白爲什麼我會死鎖。SELECT/UPDATE上的死鎖
我已經在下面的測試用例中重新創建了這種情況。
我有一個表:
CREATE TABLE [dbo].[SessionTest](
[SessionId] UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL,
[ExpirationTime] DATETIME NOT NULL,
CONSTRAINT [PK_SessionTest] PRIMARY KEY CLUSTERED (
[SessionId] ASC
) WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[SessionTest]
ADD CONSTRAINT [DF_SessionTest_SessionId]
DEFAULT (NEWID()) FOR [SessionId]
GO
我想首先選擇從這個表中的記錄,如果記錄存在設置過期時間爲當前時間加上一些間隔。它使用下面的代碼來實現:
protected Guid? GetSessionById(Guid sessionId, SqlConnection connection, SqlTransaction transaction)
{
Logger.LogInfo("Getting session by id");
using (SqlCommand command = new SqlCommand())
{
command.CommandText = "SELECT * FROM SessionTest WHERE SessionId = @SessionId";
command.Connection = connection;
command.Transaction = transaction;
command.Parameters.Add(new SqlParameter("@SessionId", sessionId));
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Logger.LogInfo("Got it");
return (Guid)reader["SessionId"];
}
else
{
return null;
}
}
}
}
protected int UpdateSession(Guid sessionId, SqlConnection connection, SqlTransaction transaction)
{
Logger.LogInfo("Updating session");
using (SqlCommand command = new SqlCommand())
{
command.CommandText = "UPDATE SessionTest SET ExpirationTime = @ExpirationTime WHERE SessionId = @SessionId";
command.Connection = connection;
command.Transaction = transaction;
command.Parameters.Add(new SqlParameter("@ExpirationTime", DateTime.Now.AddMinutes(20)));
command.Parameters.Add(new SqlParameter("@SessionId", sessionId));
int result = command.ExecuteNonQuery();
Logger.LogInfo("Updated");
return result;
}
}
public void UpdateSessionTest(Guid sessionId)
{
using (SqlConnection connection = GetConnection())
{
using (SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable))
{
if (GetSessionById(sessionId, connection, transaction) != null)
{
Thread.Sleep(1000);
UpdateSession(sessionId, connection, transaction);
}
transaction.Commit();
}
}
}
然後,如果我試圖從兩個線程中執行測試方法,他們試圖更新同一記錄我獲得以下的輸出:
[4] : Creating/updating session
[3] : Creating/updating session
[3] : Getting session by id
[3] : Got it
[4] : Getting session by id
[4] : Got it
[3] : Updating session
[4] : Updating session
[3] : Updated
[4] : Exception: Transaction (Process ID 59) was deadlocked
on lock resources with another process and has been
chosen as the deadlock victim. Rerun the transaction.
我無法理解它可能會發生使用可序列化隔離級別。我認爲首先選擇應鎖定行/表,不會讓另一個選擇獲取任何鎖。該示例是使用命令對象編寫的,但它僅用於測試目的。最初,我使用LINQ,但我想展示簡化的例子。 Sql Server Profiler顯示死鎖是鍵鎖。我將在幾分鐘內更新問題,並從sql server profiler中發佈圖表。任何幫助,將不勝感激。我明白這個問題的解決方案可能是在代碼中創建關鍵部分,但我試圖理解爲什麼可序列化隔離級別無法做到這一點。
這裏是死鎖圖形: deadlock http://img7.imageshack.us/img7/9970/deadlock.gif
在此先感謝。
+1對於一個有據可查的問題! – 2009-06-06 11:44:16