我在sql Server 2008上的SELECT / UPDATE上遇到了死锁问题.
我从这个帖子中读到了答案: SQL Server deadlocks between select/update or multiple selects但我仍然不明白为什么我会陷入僵局.
我从这个帖子中读到了答案: SQL Server deadlocks between select/update or multiple selects但我仍然不明白为什么我会陷入僵局.
我在以下测试用例中重新创建了这种情况.
我有一张桌子:
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,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,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.
我无法理解使用Serializable Isolation Level会发生什么.我认为首先选择应该锁定行/表,并且不会让另一个选择获取任何锁.该示例使用命令对象编写,但它仅用于测试目的.最初,我正在使用linq,但我想展示简化的例子. sql Server Profiler显示死锁是密钥锁.我将在几分钟内更新问题,并从sql server profiler发布图表.任何帮助,将不胜感激.我理解这个问题的解决方案可能是在代码中创建关键部分,但我试图理解为什么Serializable Isolation Level不能解决问题.
这是死锁图:
deadlock http://img7.imageshack.us/img7/9970/deadlock.gif
提前致谢.