2017-02-20 59 views
1

我想在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(); 
} 

我(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

+1

检查此q/a(http://stackoverflow.com/a/41594231/1158842)似乎是同一根本问题。如果您希望在SQL级别完成,那么您可能会前往sp_getapplock –

+0

无需首先“选择”当前值。 'UPDATE Test_Table SET intCount = intCount + 1'就足够了。当你说“* it deadlocks *”时,你实际上是否因为SQL Server检测到死锁而导致一个事务被杀死的异常?如果是,那么请将该异常的错误消息添加到您的问题中。预计线程将等待,因为您不能同时更新多个事务中的同一行,第二个更新总是需要等待前一个完成。 –

回答

1

我明白,你已经向我们展示的是问题的只是一个版本的运动简化的,但无论如何,考虑是否而非单独选择并更新你能够做到这一点作为一个单一的更新,即:

UPDATE Test_Table SET intCount += 1 

如果这是不可能的,那么将这两个语句放入单个事务中。

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

+0

感谢您的回复。我更改了代码以将存储过程的主体放入事务中,但它仍像以前一样经常死锁。 – yeerk

0

院长的回答是最正确的,但如果任何人想知道如何让正确的单独的选择和更新工作,这是我结束了代码:

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)。