如何在存储过程中设置锁定?

时间:2014-11-20 12:51:42

标签: sql-server multithreading stored-procedures

我在SQL服务器数据库上有一个长时间运行的存储过程。我不希望它每十分钟运行一次以上。

存储过程运行后,我希望将最新结果存储在LatestResult表中,与时间一致,并且对该过程的所有调用都会在接下来的十分钟内返回该结果。

这一点相对简单,但是我们发现,因为该过程检查LatestResult表并更新它,当两个用户调用该过程时,大型用户基站会出现许多死锁。同时。

在客户端/线程情况下,我会通过使用锁来解决这个问题,让第一个用户锁定函数,第二个用户遇到锁,等待结果,第一个用户完成程序调用,更新LatestResult表,然后解锁第二个用户,然后第二个用户从LatestResult表中获取结果。

有没有办法在SQL Server中实现这种锁定?

修改

这基本上是代码看起来没有错误检查调用的方式:

DECLARE @LastChecked AS DATETIME
DECLARE @LastResult AS NUMERIC(18,2)
SELECT TOP 1 @LastChecked = LastRunTime, @LastResult = LastResult FROM LastResult

DECLARE @ReturnValue AS NUMERIC(18,2)

IF DATEDIFF(n, @LastChecked, GetDate()) >= 10 OR NOT @LastResult = 0
BEGIN 
    SELECT @ReturnValue = ABS(ISNULL(SUM(ISNULL(Amount,0)),0)) FROM Transactions WHERE ISNULL(DeletedFlag,0) = 0 GROUP BY GroupID ORDER BY ABS(ISNULL(SUM(ISNULL(Amount,0)),0))
        UPDATE LastResult SET LastRunTime = GETDATE(), LastResult = @ReturnValue
        SELECT @ReturnValue
    END
ELSE
BEGIN
    SELECT @LastResult
END

我不确定分组是怎么回事,但是我发现了一个测试系统,执行时间大约是4秒。

我认为有一些工作计划存档其中一些记录并将其归结为运行总计,这可能有助于确定这四秒钟表中有数百万行......

1 个答案:

答案 0 :(得分:4)

这是使用应用程序锁定的有效机会(请参阅sp_getapplocksp_releaseapplock),因为它是对您定义的概念进行锁定,而不是对任何给定表中的任何特定行进行锁定。这个想法是你创建一个事务,然后创建这个具有一个untifier的任意锁,其他进程将等待输入那段代码,直到锁被释放。这与app层的lock()类似。 @Resource参数是任意"概念"的标签。在更复杂的情况下,您甚至可以在其中连接CustomerID或其他内容,以实现更精细的锁定控制。

DECLARE @LastChecked DATETIME,
        @LastResult NUMERIC(18,2);
DECLARE @ReturnValue NUMERIC(18,2);

BEGIN TRANSACTION;
EXEC sp_getapplock @Resource = 'check_timing', @LockMode = 'Exclusive';

SELECT TOP 1 -- not sure if this helps the optimizer on a 1 row table, but seems ok
       @LastChecked = LastRunTime,
       @LastResult = LastResult
FROM LastResult;

IF (DATEDIFF(MINUTE, @LastChecked, GETDATE()) >= 10 OR @LastResult <> 0)
BEGIN 
   SELECT @ReturnValue = ABS(ISNULL(SUM(ISNULL(Amount, 0)), 0))
   FROM   Transactions
   WHERE  DeletedFlag = 0
   OR     DeletedFlag IS NULL;

   UPDATE LastResult
   SET    LastRunTime = GETDATE(),
          LastResult = @ReturnValue;
END;
ELSE
BEGIN
   SET @ReturnValue = @LastResult; -- This is always 0 here
END;

SELECT @ReturnValue AS [ReturnValue];

EXEC sp_releaseapplock @Resource = 'check_timing';
COMMIT TRANSACTION;

你需要自己管理错误/ ROLLBACK(如链接的MSDN文档中所述),所以放入通常的TRY / CATCH。但是,这确实可以让您管理情况。

如果对这个过程存在任何争议,那么在锁定资源之后立即执行查找就不应该是单行表中的SELECT,然后是(理想情况下)只是一个IF语句如果10分钟计时器没有经过,则返回最后的已知值。因此,大多数电话应该很快处理。

请注意: sp_getapplock / sp_releaseapplock应谨慎使用;应用程序锁定绝对非常方便(例如在这种情况下),但它们只应在绝对必要时使用。