【发布时间】:2013-03-13 02:49:42
【问题描述】:
我正在使用 C# 在 Visual Studio 2010 中编写一个 Web 应用程序。 Web 应用程序执行复杂的 SQL Server 2008 语句,如果同时多次调用同一个 .aspx 页面,有时会导致死锁。建议的解决方案是使用 SQL Server 手段来防止这些死锁,但我的问题是我不太了解它,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
【问题讨论】:
-
如果您更详细地描述脚本正在做什么以及您认为可能导致死锁的原因,您可能会得到更好的答案。实际的脚本也会有所帮助。
-
@SimonWhitehead:添加了代码示例。谢谢。
-
如果这可能会导致一个死锁,我不会感到惊讶。您可能希望选择临时表,而不是 CTE,然后从中插入选择。这样,select 和 insert 会锁定不同的表。
-
顺便说一下,在 ASP.NET 中锁定很棘手。锁定仅限于单个应用程序域的范围,因此根据您的 Web 服务器的情况,其他请求可能会或可能不会看到锁定。此外,它会阻碍 Web 服务器的最佳方面之一,即同时处理多个请求。
-
我建议在数据库级别解决这个问题。如果您在应用程序层执行此操作,它将根本无法很好地扩展。我会首先检查以确保您的索引设置正确并以最佳水平执行。确保您在事务内部运行。验证任何热点的执行计划,然后开始寻找其他替代方案(尝试/捕获/重试逻辑与死锁等)。其他选项正在考虑更改在表级别使用页面锁定的方式(一种方法是禁用页面锁定并改用行级锁定)。
标签: c# asp.net sql-server sql-server-2008 locking