我正在使用C#在Visual Studio 2010中编写Web应用程序。 Web应用程序执行复杂的SQL Server 2008语句,如果同时多次调用相同的.aspx页面,有时会导致死锁。建议的解决方案是使用SQL服务器手段来防止这些死锁,但我的问题是我并不是很了解它,这对于C#来说并不是真的,我知道的方式更好。
所以我想知道,我在ASP.NET页面中使用锁(或者是C#中的锁,或者是名为mutex的Windows)而不是通过SQL服务器进行锁定以防止这些死锁,这有什么缺点?
PS。有问题的SQL Server数据库仅由此Web应用程序使用。
编辑:以下是执行SQL语句的C#代码:
int iNumRows = 0;
using (SqlConnection cn = new SqlConnection(strConnection))
{
cn.Open();
using (SqlCommand cmd = new SqlCommand(strSQL, cn))
{
//Use C# lock here
iNumRows = Convert.ToInt32(cmd.ExecuteScalar());
//Release C# lock here
}
}
这是一个SQL示例(实际上是由C#脚本动态编写的):
SET XACT_ABORT ON;
BEGIN TRANSACTION;
DELETE FROM [dbo].[t_Log_2]
WHERE [idtm]<'2011-03-12 08:41:57';
WITH ctx AS(
SELECT MIN([idtm]) AS mdIn,
MAX([odtm]) AS mdOut
FROM [dbo].[t_Log_2]
WHERE [type] = 0
AND [state] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [odtm] >= '2013-03-11 06:33:32'
AND [idtm] <= '2013-03-11 06:43:12'
)
INSERT INTO [dbo].[t_Log_2]
([oid],[idtm],[odtm],[type],[state],[huid],
[cnm],[cmdl],[batt],[dvtp0],[dvtp1])
SELECT
2,
CASE WHEN mdIn IS NOT NULL
AND mdIn < '2013-03-11 06:33:32'
THEN mdIn
ELSE '2013-03-11 06:33:32'
END,
CASE WHEN mdOut IS NOT NULL
AND mdOut > '2013-03-11 06:43:12'
THEN mdOut
ELSE '2013-03-11 06:43:12'
END,
0,
0,
N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4',
null,
null,
0,
1,
null
FROM ctx
SELECT ROWCOUNT_BIG()
DELETE FROM [dbo].[t_Log_2]
WHERE [type] = 0
AND [state] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [odtm] >= '2013-03-11 06:33:32'
AND [idtm] <= '2013-03-11 06:43:12'
AND [id] <> SCOPE_IDENTITY()
DELETE FROM [dbo].[t_Log_2]
WHERE [type] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [idtm] >= (SELECT [idtm] FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY())
AND [odtm] <= (SELECT [odtm] FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY())
AND [id] <> SCOPE_IDENTITY()
;WITH ctx1 AS(
SELECT [idtm] AS dI
FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY()
)
UPDATE [dbo].[t_Log_2]
SET [odtm] = ctx1.dI
FROM ctx1
WHERE [id] <> SCOPE_IDENTITY()
AND [type] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [idtm] < ctx1.dI
AND [odtm] > ctx1.dI
;WITH ctx2 AS(
SELECT [odtm] AS dO
FROM [dbo].[t_Log_2]
WHERE [id] = SCOPE_IDENTITY()
)
UPDATE [dbo].[t_Log_2]
SET [idtm] = ctx2.dO
FROM ctx2
WHERE [id] <> SCOPE_IDENTITY()
AND [type] = 0
AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4'
AND [idtm] < ctx2.dO
AND [odtm] > ctx2.dO
COMMIT TRANSACTION;
SET XACT_ABORT OFF
答案 0 :(得分:7)
我使用ASP.NET页面中的锁(或C#中的锁,或者名为mutex的Windows)而不是通过SQL服务器进行锁定来防止这些死锁,这有什么缺点?
不会导致死锁,而是会造成活锁。
当等待图包含一个循环(A等待B,B等待A)时发生死锁。 SQL Server定期检查所有等待图并查找周期。当检测到一个这样的循环时,通过选择受害者并中止它的交易来打破循环。
如果您将其中一些锁移到SQL Server控制的域之外,即。在进程互斥体,关键部分,C#事件或其他什么时候,等待图循环仍然会发生,但现在循环将通过应用程序完成,因此SQL Server将无法检测到(A在SQL中等待B,但是B等待A在应用程序中)。由于死锁监视器不会看到循环,它将不会运行死锁解析算法(选择受害者,中止它的事务)并且死锁将永远保持。恭喜,现在您的应用程序只是挂起而不是引发死锁异常!
你不必接受我的话,其他更有经验的人已经被这个问题烧掉并且学到了很多东西,但幸运的是写了这篇文章,这样你就可以学到简单的方法。 This very site you're reading is an example
一旦理解了问题,解决SQL Server中的死锁就相当容易了。如果您capture and attach the deadlock graph(XML,而不是图片!),以及表格的确切定义,也许我们可以提供帮助。唉,you already ignored such request所以我想唯一要问的问题是你想要更多绳子吗?
答案 1 :(得分:1)
如果不了解足够的细节,找不到真正导致死锁的地方,我猜可能是因为键范围导致了死锁,这意味着表t_log_2的索引发生了死锁,因为你有删除和更新,绝对是它们不会发生在同一行,但它们可能发生在相同的键范围内,或者一个进程可以保持A范围,请求B范围,另一个进程可以保持B范围并请求A范围。您可以使用SQL事件探查器跟踪死锁并查看其确切发生的位置。或者,简单地说,如果它不会对您的性能造成太大影响,您可以将事务隔离级别设置为[可重复读取]甚至[可序列化]。
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
....
BEGIN TRANSACTION
....