单表存储过程死锁

时间:2017-02-20 08:03:18

标签: sql sql-server

我正在尝试增加MSSQL中的值(例如,在购买礼品卡后增加用户的余额)。

我的存储过程和表格如下:

CREATE TABLE Test_Table ([intCount] [int] NOT NULL)

ALTER PROCEDURE Test_Proc AS
BEGIN
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRAN
        UPDATE Test_Table
        SET intCount = intCount + 1 
    COMMIT TRAN
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

    RETURN(0)
END

为简单起见,我在表中只有一行,我只是递增所有行。

我在C#中生成10个线程,并在每个线程中调用存储过程10次。但是我在大多数线程中遇到了死锁。我调用此存储过程的代码如下所示:

for (int thread = 0; thread < threads; thread++)
{
    new Thread(() =>
    {
        try
        {
            for (int ix = 0; ix < count; ix++)
            {
                using (var conn = new SqlConnection(connectionString))
                {
                    conn.Open();
                    SqlCommand cmd = new SqlCommand("Test_Proc", conn);
                    cmd.CommandType = CommandType.StoredProcedure;

                    cmd.ExecuteNonQuery();
                }
            }
        }
        catch (Exception e)
        {
            errors++;
        }
    }).Start();
}

我试过WITH(UPDLOCK,HOLDLOCK),但这似乎并没有减少死锁的频率。

无论如何我可以更改存储过程以防止这些死锁吗?我真的在寻找一个SQL答案,而不是仅仅在C#中序列化所有存储过程调用。

(这类似于Deadlock with single stored procedure and multiple threads,但是这个问题明确要求如何解锁,而我只是想避免死锁。)

编辑:我修改了代码以将存储过程的内容放在事务中,但它仍然是死锁。

编辑:错误消息类似于Transaction (Process ID 124) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

编辑:我根据反馈更新了存储过程,但它仍然死锁。

编辑:看起来解释如下:https://stackoverflow.com/a/36831413/1117119。 SERIALIZABLE获取共享锁,然后在写入时将其转换为独占锁。这会导致死锁。

此外,似乎还会发生任何进一步的死锁,因为我只有太多的线程:SQL Server 2008: Getting deadlocks... without any locks

2 个答案:

答案 0 :(得分:1)

我知道您向我们展示的内容只是问题的简化版本,但无论如何,请考虑是否可以将其作为单个UPDATE进行单独的SELECT和UPDATE,即:< / p>

UPDATE Test_Table SET intCount += 1  

如果无法做到这一点,请将两个语句都包含在一个事务中。

编辑: 要避免转换锁定死锁,请将with(xlock, tablock)提示添加到SELECT语句。

答案 1 :(得分:0)

Dean的答案是最正确的,但是如果其他人想知道如何使单独的SELECT和UPDATE正常工作,这就是我最终得到的代码:

BEGIN TRAN

    DECLARE @x int

    select @x = intCount
    from Test WITH (TABLOCKX, UPDLOCK)
    WHERE id = 1    

    UPDATE Test
    SET intCount = @x + 1
    WHERE id = 1    

COMMIT TRAN

这解决了第一个选择不进行独占锁定(https://stackoverflow.com/a/36831413/1117119)的问题。

如果创建了太多线程(SQL Server 2008: Getting deadlocks... without any locks),看起来任何获取锁的代码都会死锁。