1

我正在使用 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
4

2 回答 2

7

我在 ASP.NET 页面中使用锁(或 C# 中的锁,或名为 mutex 的 Windows)而不是通过 SQL 服务器进行锁定以防止这些死锁有什么缺点?

您将不会导致死锁,而是会导致活锁。

当等待图包含一个循环(A 在 B 上等待,B 在 A 上等待)时,就会发生死锁。SQL Server 定期检查所有等待图并查找周期。当检测到一个这样的循环时,通过选择受害者并中止它的事务来打破循环。

如果您将其中一些锁移到 SQL Server 控制领域之外,即。在进程互斥锁、临界区、C# 事件或其他任何情况下,等待图周期仍将发生,但现在该周期将通过应用程序完成,因此 SQL Server 将无法检测到它(A 在 SQL 中等待 B,但 B 等待 A在应用程序中)。由于死锁监视器不会看到一个循环,它不会运行死锁解决算法(选择一个受害者,中止它的事务)并且死锁将永远存在。恭喜,现在您的应用程序只是挂起而不是引发死锁异常!

你不必相信我的话,其他更有经验的人已经被这个问题所困扰并学习了困难的方法,但幸运的是写了它,这样你就可以学习简单的方法。您正在阅读的这个网站就是一个例子

一旦了解了问题,解决 SQL Server 中的死锁就相当容易了。如果您捕获并附加死锁图(XML,而不是图片!)以及您的表的确切定义,也许我们可以提供帮助。唉,你已经忽略了这样的要求,所以我想唯一要问的问题是你想要更多的绳子吗?

于 2013-03-13T07:40:15.847 回答
1

在不知道足够细节的情况下,找不到真正导致死锁的地方,我猜可能是因为键范围导致死锁,这意味着死锁发生在表 t_log_2 的索引上,因为你有删除和更新,肯定不是发生在同一行,但它们可以发生在同一键范围内,或者一个进程可以持有 A 范围,请求 B 范围,另一个进程可以持有 B 范围并请求 A 范围。您可以使用 SQL Profiler 跟踪死锁并查看它到底发生在哪里。或者,简单地说,如果它不会对您的性能造成太大影响,您可以将事务隔离级别设置为 [repeatable read] 甚至 [serializable]。


SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
....
BEGIN TRANSACTION
....

于 2013-03-13T04:22:31.123 回答