在SELECT语句中使用XLOCK(Exclusive Lock)被认为是不好的做法吗?
让我们假设一个简单的场景,即客户的账户余额为40美元。两个并发的20美元购买请求到达。交易包括:
所以没有XLOCK:
但帐户中应该还有0美元。
有没有办法在不使用XLOCK的情况下阻止这种情况?有哪些替代方案?
答案 0 :(得分:0)
执行更新时,应直接更新到数据项以防止出现这些问题。下面的示例代码演示了一种安全的方法:
CREATE TABLE #CustomerBalance (CustID int not null, Balance decimal(9,2) not null)
INSERT INTO #CustomerBalance Values (1, 40.00)
DECLARE @TransactionAmount decimal(9,2) = 19.00
DECLARE @RemainingBalance decimal(9,2)
UPDATE #CustomerBalance
SET @RemainingBalance = Balance - @TransactionAmount,
Balance = @RemainingBalance
SELECT @RemainingBalance
(No column name)
21.00
此方法的一个优点是,只要UPDATE
语句开始执行,就会锁定该行。如果两个用户同时更新值"同时",由于数据库如何工作,将开始在另一个之前更新数据。第一个UPDATE
将阻止第二个UPDATE
操纵数据,直到第一个完成。当第二个UPDATE
开始处理记录时,它将看到第一次更新时已更新到余额中的值。
作为这样做的副作用,您需要在更新后使用代码检查余额,如果您有"透支"则回滚该值。平衡,或任何必要的。这就是为什么这个示例代码返回变量@RemainingBalance中的剩余余额。
答案 1 :(得分:0)
根据您放置查询的方式,隔离级别READ COMMITTED
应该完成这项工作。
假设要执行以下代码:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
start transaction;
update account set balance=balance-20 where accountid = 'XY';
commit;
假设T1执行语句update account set balance=balance-20 where accountid = 'XY'
;它将使用accountid ='XY'在记录上放置一个写锁定。
如果现在第二个事务T2在T1提交之前执行相同的语句,则阻止T2的语句直到T1提交。
之后,T2继续。最后,余额将减少40
。
答案 2 :(得分:0)
您的问题是基于使用XLOCK
是一种不好的做法的假设。虽然将这个提示始终放在任何地方都是最正确的方法是正确的,但是没有其他方法可以在您的特定情况下实现所需的功能。
当我遇到同样的问题时,我发现在同一事务中放置在验证XLOCK, HOLDLOCK
中的select
组合通常可以完成工作。 (我有一个执行所有必要验证的存储过程,然后只有在一切正常时才更新Accounts表。是的,在单个事务中。)
然而,有一个重要的警告:如果您的数据库启用了RCSI,其他读者将能够通过从版本存储中获取先前的值来通过锁定。在这种情况下,添加READCOMMITTEDLOCK
会关闭相关行的乐观版本控制,并将行为恢复为标准RC。