Microsoft SQL Server 2008 上使用 TABLOCK、XLOCK 命令的间歇性死锁

Posted

技术标签:

【中文标题】Microsoft SQL Server 2008 上使用 TABLOCK、XLOCK 命令的间歇性死锁【英文标题】:Intermittent Deadlock on Microsoft SQL Server 2008 With TABLOCK, XLOCK Commands 【发布时间】:2012-10-15 16:35:55 【问题描述】:

以下存储过程出现间歇性死锁。它每分钟运行一次。它已经投入生产 1 年多,我们通常不会收到此错误,但有时会突然出现......它在我们的一个环境中每天抛出 3 到 5 次异常......它与其他实例的环境相同没有抛出错误。存储过程被制作成一次只能运行一次,但也许我们这样做的方式不正确?

这是错误:

System.Web.HttpUnhandledException:引发了“System.Web.HttpUnhandledException”类型的异常。 ---> System.Data.SqlClient.SqlException:事务(进程 ID 60)是 与另一个进程在锁定资源上死锁并已被选中 作为僵局的受害者。重新运行事务。

这是存储过程:

 SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[requestUpdate]
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRAN
    DECLARE @StartDate datetime,
            @EndDate datetime,
            @Throttle bit,
            @Expired bit

    SELECT @Throttle = 0
    SELECT @Expired = 0

    SELECT TOP 1 @StartDate = uq.StartDate, @EndDate = uq.EndDate
      FROM UpdateQueue uq WITH(TABLOCK,XLOCK)
     ORDER BY ID DESC

    -- PREVENT ANOTHER REQUEST IF THIS SP HAS BEEN
    -- CALLED IN THE LAST FORTY SECONDS.
    IF DATEADD(SECOND,-40,GETDATE()) < @StartDate
    BEGIN
      SELECT @Throttle = 1
    END

    -- CREATE ANOTHER REQUEST IF THE CURRENT ONE
    -- HAS NOT COMPLETED IN THE LAST FIVE MINUTES.
    IF @StartDate <= DATEADD(MINUTE,-5,GETDATE())
    BEGIN
      SELECT @Expired = 1
    END

    -- HAS THE CURRENT REQUEST EXPIRED?
    IF @EndDate IS NULL AND @Expired = 1
    BEGIN
      INSERT INTO UpdateQueue (RequestID, StartDate) OUTPUT 'EXPIRED' AS Result, INSERTED.RequestID AS RequestID
      VALUES (NEWID(), GETDATE())
    END

    -- HAS THE CURRENT REQUEST COMPLETED AND YOU ARE NOT THROTTLING
    -- OR HAVE THERE NOT BEEN ANY REQUESTS YET?
    ELSE IF (@EndDate IS NOT NULL AND @Throttle = 0) OR @StartDate IS NULL
    BEGIN
      INSERT INTO UpdateQueue (RequestID, StartDate) OUTPUT 'STARTED' AS Result, INSERTED.RequestID AS RequestID
      VALUES (NEWID(), GETDATE())
    END

    -- Running
    ELSE
    BEGIN
      SELECT 'RUNNING' AS Result, NULL AS RequestID
    END

COMMIT

【问题讨论】:

你应该使用app locks来实现sp执行排除,而不是表X锁。关于死锁:捕获并发布死锁图(XML,不是图片!),请参阅Save Deadlock Graphs。 【参考方案1】:

我怀疑这是一个特定的锁被持有的时间超过了您的连接愿意等待的时间。如果没有东西可以终止长时间运行的事务,在这里,您将遇到这种情况,因为当连接超时时,SQL Server 会强制等待结束并终止作为死锁受害者的传入锁。

长话短说:某个进程(此处未详述)正在锁定表,导致死锁。

检查当您遇到死锁时还发生了什么 - 是否有备份正在运行,或者其他什么东西锁定了这个表?

【讨论】:

以上是关于Microsoft SQL Server 2008 上使用 TABLOCK、XLOCK 命令的间歇性死锁的主要内容,如果未能解决你的问题,请参考以下文章