sql-server-2008 – SELECT/UPDATE上的死锁
我在SQL Server 2008上的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,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不能解决问题. 这是死锁图: 提前致谢. 解决方法它不足以具有可序列化的事务,您需要提示锁定以使其工作.可序列化隔离级别通常仍会获得“最弱”类型的锁定,以确保满足可序列化条件(可重复读取,没有幻像行等) 因此,您正在抓取桌面上的共享锁,您稍后(在可序列化的事务中)尝试升级到an update lock.如果另一个线程持有共享锁,则升级将失败(如果没有其他人保持共享,它将起作用锁). 您可能希望将其更改为以下内容: SELECT * FROM SessionTest with (updlock) WHERE SessionId = @SessionId 这将确保在执行SELECT时获取更新锁定(因此您不需要升级锁定). (编辑:晋中站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |