简体   繁体   English

我可以使用C#锁来防止Web应用程序中的SQL Server死锁吗?

[英]Can I use C# locks to prevent SQL Server deadlocks in my web application?

I am writing a web application in Visual Studio 2010 using C#. 我正在使用C#在Visual Studio 2010中编写Web应用程序。 The web app executes complex SQL Server 2008 statements that sometimes result in a deadlock if the same .aspx page is called more than once at the same time. 该Web应用程序执行复杂的SQL Server 2008语句,如果一次同时调用同一.aspx页不止一次,则有时会导致死锁。 The proposed solution is to use SQL server means to prevent these deadlocks, but my issue is that I don't really understand it well, which is not true about C#, that I know way better. 提出的解决方案是使用SQL Server手段来防止这些死锁,但是我的问题是我不太了解它,对于C#来说这不是事实,我知道得更好。

So I'm wondering, what is the downside of me using locks in the ASP.NET page (or locks in C#, or Windows named mutex) instead of doing the locking through SQL server to prevent these deadlocks? 所以我想知道,使用ASP.NET页中的锁(或C#中的锁,或名为互斥锁的Windows)对我有什么不利之处,而不是通过SQL Server进行锁定以防止这些死锁?

PS. PS。 The SQL Server database in question is used only by this web application. 该SQL Server数据库仅由该Web应用程序使用。

EDIT: The following is C# code that executes SQL statements: 编辑:以下是执行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
    }
}

And here's a sample of SQL (that in reality is dynamically composed by C# script): 这是一个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

what is the downside of me using locks in the ASP.NET page (or locks in C#, or Windows named mutex) instead of doing the locking through SQL server to prevent these deadlocks? 使用ASP.NET页中的锁(或C#或Windows中称为互斥锁的锁)而不是通过SQL Server进行锁定以防止这些死锁的缺点是什么?

Instead of causing deadlocks you will cause livelocks. 不会造成死锁,而是会导致死锁。

A deadlock occurs when the wait graph contains a cycle (A wait on B, B waits on A). 当等待图包含一个周期(A在B上等待,B在A上等待)时,将发生死锁。 SQL Server periodically inspects all the wait graphs and looks for cycles. SQL Server定期检查所有等待图并查找周期。 When one such cycle is detected the cycle is broken by choosing a victim and aborting it's transaction. 当检测到一个这样的周期时,通过选择受害者并中止其交易来中断该周期。

If you move some of these locks outside of the SQL Server controlled realm, ie. 如果将其中一些锁移出SQL Server控制的领域,即。 in process mutexes, critical sections, C# events or whatever, the wait graphs cycles will still occur but now the cycle will complete through the app, thus it will be undetectable by SQL Server (A waits for B in SQL, but B waits for A in the app). 在进程互斥体,关键部分,C#事件或任何其他情况下,仍将发生等待图循环,但是现在该循环将通过应用程序完成,因此SQL Server将无法检测到它(A等待SQL中的B,但是B等待A在应用中)。 Since the deadlock monitor will not see a cycle, it will not run the deadlock resolving algorithm (choose a victim, abort it's transaction) and the deadlock will stay on forever. 由于死锁监视器不会看到周期,因此它将不会运行死锁解析算法(选择受害者,中止其事务),并且死锁将永远保持下去。 Congratulations, now your application simply hangs instead of raising a deadlock exception! 恭喜,现在您的应用程序只是挂起,而不是引发死锁异常!

You don't have to take my word from it, other more experienced have already been burned by this issue and learned the hard way, but fortunately wrote about it so you can learn the easy way. 您不必信服我的话,这个问题已经使其他经验丰富的人烦恼了,并且学到了艰辛的方法,但是幸运的是写了此书,以便您可以学习简单的方法。 This very site you're reading is an example . 您正在阅读的这个站点就是一个例子

Solving deadlocks in SQL Server is fairly easy once you understand the issue. 了解问题后,解决SQL Server中的死锁就相当容易了。 If you capture and attach the deadlock graph (the XML, not the picture!), along with the exact definition of your tables, perhaps we can help. 如果您捕获并附加死锁图 (XML,而不是图片!)以及表的确切定义,也许我们可以提供帮助。 Alas, you already ignored such request so I guess the only question to ask is Would you like more rope? las, 您已经忽略了这样的要求,所以我想唯一要问的问题是您是否还想要绳子?

Without knowing enough detail, could not find where it really cause deadlock, I can guess it probably because of key range caused the deadlock, which means deadlock happened on the index of table t_log_2, since you have the delete and update, definitely they are not happening on same row, but they can happen on same key range, or one process can hold A range, request B range, and another process can hold B range and request A range. 没有足够的细节,找不到真正导致死锁的位置,我可以猜测可能是由于键范围导致了死锁,这意味着死锁发生在表t_log_2的索引上,因为您具有删除和更新功能,所以它们肯定不是发生在同一行上,但它们可以发生在同一键范围上,或者一个进程可以容纳A范围,请求B范围,而另一个进程可以容纳B范围并请求A范围。 You can use SQL Profiler to trace the deadlock and see where it exactly happened. 您可以使用SQL事件探查器跟踪死锁并查看死锁的确切位置。 Or, simply, if it doesn't hurt your performance too much, you can set transaction isolation level to [repeatable read] or even [serializable]. 或者,简单地说,如果它不会对您的性能造成太大影响,则可以将事务隔离级别设置为[可重复读取]甚至[可序列化]。


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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM