HOLDLOCK XLOCK ROWLOCK 有时不工作
HOLDLOCK XLOCK ROWLOCK not working some times
我有以下情况:银行环境,我不希望特定客户完成从他的帐户中取款,除非之前的取款没有提交。
为了实现这一点,我创建了以下 table
CREATE TABLE [dbo].[Locks](
[CustomerID] [int] NOT NULL,
CONSTRAINT [PK_Locks] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)
现在,每当提款开始时,我都有以下代码,基本上将客户插入此助手 table(如果他不存在),然后在交易期间锁定该行,以便在第一笔交易提交之前不会发生其他取款
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 可能无法锁定事务有什么想法吗?
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 约束,fail.This 就是最幸福的情况。
- 一个事务可以来,运行 SELECT,断定有一行并进行第二个SELECT。同时,该行可以删除。 SELECT WITH (lock hints) 将找不到任何行,但仍继续得出它锁定了某些内容的结论(它没有,没有要锁定的行)。这是一个更糟糕的情况,它可能导致(滚鼓,拜托!)负余额。
这些在大约 10 秒的代码检查中出现。我敢肯定还有更多(我什至没有考虑回滚......)。使用行作为锁是一种反模式。 Use applocks instead.
我有以下情况:银行环境,我不希望特定客户完成从他的帐户中取款,除非之前的取款没有提交。
为了实现这一点,我创建了以下 table
CREATE TABLE [dbo].[Locks](
[CustomerID] [int] NOT NULL,
CONSTRAINT [PK_Locks] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)
现在,每当提款开始时,我都有以下代码,基本上将客户插入此助手 table(如果他不存在),然后在交易期间锁定该行,以便在第一笔交易提交之前不会发生其他取款
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 可能无法锁定事务有什么想法吗?
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 约束,fail.This 就是最幸福的情况。
- 一个事务可以来,运行 SELECT,断定有一行并进行第二个SELECT。同时,该行可以删除。 SELECT WITH (lock hints) 将找不到任何行,但仍继续得出它锁定了某些内容的结论(它没有,没有要锁定的行)。这是一个更糟糕的情况,它可能导致(滚鼓,拜托!)负余额。
这些在大约 10 秒的代码检查中出现。我敢肯定还有更多(我什至没有考虑回滚......)。使用行作为锁是一种反模式。 Use applocks instead.