HOLDLOCK XLOCK ROWLOCK无法工作一段时间

时间:2015-02-05 12:23:40

标签: sql sql-server transactions

我有以下情况:银行上下文,我不希望特定客户完成从他的帐户中提款,除非之前的提款没有提交。

为了达到这个目的,我创建了下表

CREATE TABLE [dbo].[Locks](
        [CustomerID] [int] NOT NULL,
    CONSTRAINT [PK_Locks] PRIMARY KEY CLUSTERED 
    (
        [CustomerID] ASC
    )

现在,每当撤销开始时,我都有以下代码,基本上将客户插入此帮助程序表(如果他不存在),然后在事务持续时间内锁定行,以便不会发生其他撤销在第一个交易提交之前

BEGIN TRAN
IF NOT EXISTS (SELECT * FROM Locks WHERE CustomerID=@customerId) --if customerid does not exist, insert the row
BEGIN
    INSERT INTO Locks (CustomerID) 
    VALUES (@customerId)
END
SELECT CustomerID FROM Locks WITH (HOLDLOCK XLOCK ROWLOCK) WHERE CustomerID=@customerId --lock on row
--(check if customer has enough balance, then perform withdraw from customer account)
COMMIT

以上代码似乎一般都有工作,每天有数千次撤销,但每周左右一次,我确实得到了一个锁无法正常工作的情况,并且客户被发现存在负余额,因为两次撤销操作发生在同时。

HOLDLOCK XLOCK ROWLOCK可能无法锁定事务的任何想法?

1 个答案:

答案 0 :(得分:4)

IF NOT EXISTS (SELECT * FROM Locks WHERE CustomerID=@customerId) --if customerid does not exist, insert the row
BEGIN
    INSERT INTO Locks (CustomerID) 
    VALUES (@customerId)
END
SELECT CustomerID FROM Locks WITH (HOLDLOCK XLOCK ROWLOCK) WHERE CustomerID=@customerId --lock on row
--(check if customer has enough balance, then perform withdraw from customer account)

这是一个竞争条件场

  • 两个事务都可以运行SELECT,结束没有锁,并且都继续插入锁。如果有PK约束,那么就会失败。这是一个幸福的案例。
  • 一个事务可以来,运行SELECT,得出一行并继续第二个SELECT。同时该行可以删除。 SELECT WITH(锁定提示)将找不到行,但仍然会得出锁定某些内容的结论(它没有,没有要锁定的行)。这是一个更糟糕的情况,它可能导致(滚动,请!)负余额

这些出现在10秒的代码检查中。我确信还有更多(我甚至没有考虑回滚......)。将行用作锁是一种反模式。 Use applocks instead