2013-03-12 19 views
1

我正在学习更复杂的SQL Server 2008技术,所以如果我问太明显的问题,我会提前道歉。t-SQL复合语句导致死锁,任何想法为什么?

我有这样创建如下表:

CREATE TABLE [dbo].[t_Log_2] 
(
    [id] INT NOT NULL IDENTITY(1,1) PRIMARY KEY, 
    [oid] INT, 
    [idtm] DATETIME2, 
    [odtm] DATETIME2, 
    [type] TINYINT, 
    [state] TINYINT, 
    [huid] UNIQUEIDENTIFIER, 
    [cnm] NVARCHAR(256), 
    [cmdl] NVARCHAR(256), 
    [batt] TINYINT, 
    [dvtp0] SMALLINT, 
    [dvtp1] SMALLINT 
); 

CREATE INDEX idx_idt 
      ON [dbo].[t_Log_2]([idtm]); 

CREATE INDEX idx_odt 
      ON [dbo].[t_Log_2]([odtm]); 

CREATE INDEX idx_huid 
      ON [dbo].[t_Log_2]([huid]); 

CREATE INDEX idx_cnm 
      ON [dbo].[t_Log_2]([cnm]); 

然后将下面的查询可以从几个并发线程从ASP.NET Web应用程序运行。请注意,此整个查询需要运行原子

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 

注意,上面的查询被复制1对1的从动态构成它的C#代码。实际上,它的参数并非如上所示的硬编码。

该查询工作在大部分时间,但一旦我得到的日志中出现以下错误一阵:

事务(进程ID 80)已被死锁的锁资源与 另一个进程,并已被选作死锁受害者。重新运行 交易。

任何想法我该怎么做才能防止这种僵局?

+1

在您的选择上使用UPDLOCK提示。 – Arvo 2013-03-12 09:21:41

+5

捕获并附加死锁图形(XML,不是它的图片!)请参见[保存死锁图形(SQL Server Profiler)](http://msdn.microsoft.com/zh-cn/library/ms190465.aspx) – 2013-03-12 09:28:44

+0

@Arvo:你能解释一下多一点吗? – c00000fd 2013-03-12 09:29:20

回答

0

您或者需要持有更多的锁或更少的锁。

最简单的答案是去NOLOCK(最佳性能)或TABLOCKX(一致性不用考虑)。

如果因为一致性要求不能使用with (nolock),可以添加with (tablockx)。 这实际上意味着一次只有一个线程可以执行类似语句 - 不会有并发性。

另一种方法是分析你的要求有更详细的,不能没有理解为什么要更新的表来完成,哪些数据是等

例如,做这种说法真的需要在交易中?它闻起来像管家:

DELETE FROM [dbo].[t_Log_2] 
    WHERE [idtm]<'2011-03-12 08:41:57'; 

如果您采取了上述交易,并把它放在一个单独的批处理,您可能会发现问题消失。

+1

很少是答案。只需使用READ COMMITTED,它不会保持锁定,只需要释放它们。不会以主要方式促成僵局。 – usr 2013-03-12 11:00:58