【发布时间】:2017-02-20 08:03:17
【问题描述】:
我正在尝试增加 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。
【问题讨论】:
-
检查这个 q/a (stackoverflow.com/a/41594231/1158842) 似乎是同一个根本问题。如果您希望在 SQL 级别完成,那么您可能会前往 sp_getapplock
-
不需要先
select当前值。UPDATE Test_Table SET intCount = intCount + 1就足够了。当你说“it deadlocks”时,你是否真的得到一个异常,因为 SQL Server 检测到死锁而导致一个事务被终止?如果是,那么请将该异常的错误消息添加到您的问题中。预计线程会等待,因为您不能同时从多个事务中更新同一行,第二次更新总是需要等待前一次完成。
标签: sql sql-server