我有以下情况:银行上下文,我不希望特定客户完成从他的帐户中提款,除非之前的提款没有提交。
为了达到这个目的,我创建了下表
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可能无法锁定事务的任何想法?
答案 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)
这是一个竞争条件场。
这些出现在10秒的代码检查中。我确信还有更多(我甚至没有考虑回滚......)。将行用作锁是一种反模式。 Use applocks instead