sql-server-2008 – SELECT/UPDATE上的死锁

前端之家收集整理的这篇文章主要介绍了sql-server-2008 – SELECT/UPDATE上的死锁前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我在sql Server 2008上的SELECT / UPDATE上遇到了死锁问题.
我从这个帖子中读到了答案: 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

提前致谢.

解决方法

它不足以具有可序列化的事务,您需要提示锁定以使其工作.

可序列化隔离级别通常仍会获得“最弱”类型的锁定,以确保满足可序列化条件(可重复读取,没有幻像行等)

因此,您正在抓取桌面上的共享锁,您稍后(在可序列化的事务中)尝试升级an update lock.如果另一个线程持有共享锁,则升级将失败(如果没有其他人保持共享,它将起作用锁).

您可能希望将其更改为以下内容

SELECT * FROM SessionTest with (updlock) WHERE SessionId = @SessionId

这将确保在执行SELECT时获取更新锁定(因此您不需要升级锁定).

猜你在找的MsSQL相关文章