1

我有一个表,它有点像一个队列,除了记录永远不会从表中删除。我试图尝试让存储过程返回一条当前未从前端应用程序处理的记录。我们所拥有的是我们设置的“锁定”列来指示这一点。我们这样做的原因是每次只有一个呼叫中心代理可以处理记录。到目前为止,这是我的 sql 的样子。问题是,如果我从两个单独的会话(第二个会话注释掉 waitfor 语句)运行此查询,则第二个会话在 10 秒内不返回任何记录。选择记录时,我已将其范围缩小到 order by 子句。如果我删除 Order By 它会返回,但我需要 order by。

或者也许我的查询完全错误?我应该使用事务隔离级别(可序列化、快照)吗?任何指导都会很棒!

DECLARE @WorkItemId INT;

BEGIN TRANSACTION

/* TODO: Skip Records That Have Been Completed. */
SET @WorkItemId = (SELECT TOP 1 CampaignSetDetailId FROM CampaignSetDetail WITH (XLOCK, READPAST) WHERE LockedBy IS NULL ORDER BY NEWID(), NumAttempts ASC);

/* */
UPDATE CampaignSetDetail SET LockedBy = 'MPAUL', LockedDTM = GETUTCDATE() WHERE CampaignSetDetailId = @WorkItemId;

/* */
SELECT * FROM CampaignSetDetail WHERE CampaignSetDetailId = @WorkItemId;

WAITFOR DELAY '00:00:10';

COMMIT TRANSACTION
4

3 回答 3

0

你可以在一个声明中做到这一点

DECLARE @WorkItemId INT;

UPDATE [CampaignSetDetail]
    SET
            [LockedBy] = 'MPAUL',
            [LockedDTM] = GETUTCDATE()
    WHERE
            [CampaignSetDetailId] = (
                SELECT TOP 1
                            [CampaignSetDetailId],
                            @WorkItemId = [CampaignSetDetailId]
                    FROM
                            [CampaignSetDetail]
                    WHERE
                            [LockedBy] IS NULL
                    ORDER BY
                            [NumAttempts]);

SELECT
            * -- You shouldn't do this.
    FROM
            [CampaignSetDetail]
    WHERE
            [CampaignSetDetailId] = @WorkItemId;

我假设[CampaignSetDetailId]在表上形成聚集索引。你可以考虑这样的索引,

CREATE INDEX [IX_CampaignSetDetail_NumAttempts]
    ON [CampaignSetDetail]([NumAttempts])
    WHERE [LockedBy] IS NULL;

来优化这个操作。

于 2015-01-16T16:07:51.050 回答
0

尝试以下 - 一组重试尝试,并且还使用更新锁。

  DECLARE @RetryCount Int = 1
  DECLARE @MaxRetries Int = 5;
  SET @RetryCount = 1 
  WHILE @RetryCount < @MaxRetries
   BEGIN
      BEGIN TRY

            /* TODO: Skip Records That Have Been Completed. */
         SET @WorkItemId = (
                            SELECT TOP 1
                                    CampaignSetDetailId
                            FROM    CampaignSetDetail WITH (UPDLOCK)
                            WHERE   LockedBy IS NULL
                            ORDER BY NEWID()
                                   ,NumAttempts ASC
                           );

         UPDATE   CampaignSetDetail WITH (UPDLOCK)
         SET      LockedBy = 'MPAUL'
                 ,LockedDTM = GETUTCDATE()
         WHERE    CampaignSetDetailId = @WorkItemId;

         SELECT   *
         FROM     CampaignSetDetail WITH (UPDLOCK)
         WHERE    CampaignSetDetailId = @WorkItemId;

         SELECT   @RetryCount = @MaxRetries;
      END TRY
      BEGIN CATCH
         IF ERROR_NUMBER() IN (1204, 1205, 1222)
            BEGIN
               SET @RetryCount += 1;
               WAITFOR DELAY '00:00:02';
            END 
         ELSE
            THROW;
      END CATCH
   END  

有关特定锁定错误的更多详细信息,请参见此处。即使从其他会话执行同一张表上的更新,这也应该有效。

于 2015-01-16T15:52:49.933 回答
0

我想我找到了一个很好的解决方案,但我仍在评估所有答案。我也在尝试使用 CTE,这给了我目前最好的性能。

DECLARE @WorkItemId INT;

WITH CTE AS (
    SELECT TOP 1 [CampaignSetDetailId], LockedBy FROM [dbo].[CampaignSetDetail] WITH (ROWLOCK, READPAST) WHERE LockedBy IS NULL ORDER BY NEWID(), NumAttempts ASC
)
UPDATE CTE SET LockedBy = 'DIESEL', @WorkItemId = [CampaignSetDetailId]

SELECT @WorkItemId

WAITFOR DELAY '00:00:05';
于 2015-01-16T21:05:46.337 回答