如何在存储过程的读取/更新期间避免SQL死锁

时间:2019-06-11 16:01:58

标签: sql-server tsql

我有一个用于将排队的更改推送到另一个系统的过程。该程序是几年前创建的,经过深思熟虑,目的是防止在执行过程中出现死锁。在过去的一周中,我们开始看到僵局,但无法确定一种模式。当死锁开始发生时,它可能会持续超过一天或几个小时。迄今为止,低活动时间段显示很少或没有死锁,但是高活动时间段显示没有成千上万个死锁。在持续活跃的12小时内,没有死锁。较慢的12小时内,有4,000多个死锁。

我在现有帖子中发现的内容使我认为我应该对自己拥有的内容感到满意,而不必担心僵局,但是缺乏任何频率/发生方式的模式都难以接受。没有模式,我将无法在测试环境中进行复制。

这是发生死锁的过程。我们有多达30个服务调用此过程来处理更改。今天,我们运行缓慢,仅运行五项服务(在五台单独的服务器上),但僵局仍在继续。

ALTER PROCEDURE [dbo].[retrieveAndLockTransactionChangeLogItems]
@serverName VARCHAR(50)
AS

--Create a common-table-expression to represent the data we're looking
--for.  In this case, all the unprocessed, unlocked items for a 
--transaction.

WITH cte AS 
(
  SELECT t.* 
  FROM TransactionChangeLog t WITH (UPDLOCK, ROWLOCK, READPAST)
  WHERE t.intTransactionID = 
    (SELECT TOP 1 t1.intTransactionID
    FROM TransactionChangeLog t1 WITH (ROWLOCK, READPAST)
    INNER JOIN TransactionServices s ON 
        s.intTransactionID = t1.intTransactionID
    WHERE s.ProductionSystemDefinitionID > 2 --SoftPro v4
    AND t1.bitProcessed = 0 
    AND t1.bitLocked = 0
    AND t1.intSourceID = 1
    AND NOT EXISTS(SELECT t2.intChangeID 
                  FROM TransactionChangeLog t2 WITH (ROWLOCK, READPAST)
                  WHERE t2.intTransactionID = t1.intTransactionID 
                  AND t2.bitLocked = 1)
    AND NOT EXISTS (SELECT ts.intServiceID 
                   FROM TransactionServices ts
                   WHERE ts.intTransactionID = t1.intTransactionID 
                   AND ts.intStatusID in (0, 5, 6, 7))
    ORDER BY t1.Priority, t1.dtmLastUpdatedDate, t1.dtmChangeDate)
  AND t.bitProcessed = 0
)

--Now update those records to Locked.
UPDATE cte 
SET bitLocked = 1, 
    dtmLastUpdatedDate = GETUTCDATE(), 
    dtmLockedDate = GETUTCDATE(), 
    LockedBy = @serverName

--Use the OUTPUT clause to return the affected records. 
--We do this instead of a SELECT then UPDATE because it acts as a
--single operation and we can avoid any deadlocks.
OUTPUT 
Inserted.intChangeID AS 'ChangeID',
Inserted.intSourceID AS 'SourceID',
Inserted.bnyChangeData AS 'ChangeData',
Inserted.intChangeDataTypeID AS 'ChangeDataTypeID',
Inserted.intTransactionID AS 'TransactionID',
Inserted.intUserID AS 'UserID',
Inserted.dtmChangeDate AS 'ChangeDate',
Inserted.bitLocked AS 'Locked',
Inserted.dtmLockedDate AS 'LockedDate',
Inserted.bitProcessed AS 'Processed',
Inserted.dtmLastUpdatedDate AS 'LastUpdatedDate',
Inserted.Retries,
Inserted.LockedBy,
Inserted.ServiceID,
Inserted.Priority

根据历史记录,在过去一周之前,我预计此过程不会造成任何僵局。在这一点上,我很高兴了解我们现在为什么/如何陷入僵局。

0 个答案:

没有答案