检查约束线程安全吗?

问题描述 投票:0回答:1

可以使用检查约束代替悲观锁吗?

考虑 SQL Server 中有下表:

create table Balances(
 id int,
 userId int,
 balance money)

如果超过 1 个线程尝试更改余额,将检查约束并解决任何问题 并发问题还是应该使用锁?

另外,我想知道检查约束在其他关系数据库(例如 Postgres)中是否具有相同的行为?

sql sql-server concurrency check-constraints locks
1个回答
0
投票

我在这里回答字里行间,因为问题本身没有多大意义,但评论似乎添加了一些上下文。

我怀疑真正的问题不是

CONSTRAINT
(你应该有),而是你的逻辑,并且你可能有如下所示的东西:

IF (SELECT Balance - @TransferValue
    FROM dbo.YourTable
    WHERE AccountID = @AccountID) >= 0
BEGIN
    UPDATE dbo.YourTable
    SET Balance = Balance - @TransferValue
    WHERE AccountID = @AccountID;
END;

不管

CONSTRAINTS
,如果您同时运行 2 个线程,这可能无法按您想要的方式工作。然后我们会遇到一个潜在的竞争条件:

  1. 线程 1 检查
    Balance - @TransferValue
    的值并解析 >= 0 的值
  2. 线程 2,before 线程 1 去执行
    UPDATE
    ,还检查
    Balance - @TransferValue
    的值并解析 >= 0
  3. 的值
  4. 线程 1
    UPDATE
    Balance
    的值,将其减少到 >= 0 的值;事务完成时锁定进程中的行。
  5. 线程 2 等待线程 1 释放行/表上的锁。
  6. 线程 1 完成,释放其锁
  7. 线程 2
    UPDATES
    Balance
    的值将其减少到一个值 < 0

在这种情况下,

CONSTRAINT
将停止第二次更新的运行,但这并不能阻止上面的代码变得bad。如果您真的愿意,我们可以让它变得更糟,然后
CONSTRAINT
根本不起作用:

DECLARE @NewBalance decimal(12,4);

SELECT @NewBalance = Balance - @TransferValue
FROM dbo.YourTable
WHERE AccountID = @AccountID;

IF @NewBalance > 0
BEGIN
    UPDATE dbo.YourTable
    SET Balance = @NewBalance
    WHERE AccountID = @AccountID;
END;

现在,

CONSTRAINT
根本无法阻止
UPDATE
。事实上,
Balance
的值将是
Balance
before线程1完成的值减去线程2的传输值。

对于这样一个简单的

UPDATE
,您只需在同一个语句中处理所有内容并使用
CONSTRAINT
,是的。所以
CONSTRAINT
就是

ALTER TABLE dbo.YourTable ADD CONSTRAINT chk_YourTable_PositiveBalance CHECK (Balance >= 0);

然后你可以执行以下语句:

UPDATE dbo.YourTable
SET Balance = Balance - @TransferValue
WHERE AccountID = @AccountID;

但是,如果您因“原因”需要“离开”并事先获取余额的价值,那么您需要应用适当的锁定并使用交易。比如:

SET XACT_ABORT ON;

BEGIN TRANSACTION;

DECLARE @NewBalance decimal(12,4);

SELECT @NewBalance = Balance - @TransferValue
FROM dbo.YourTable WITH (UPDLOCK)
WHERE AccountID = @AccountID;

--Do something(s)

UPDATE dbo.YourTable
SET Balance = @NewBalance
WHERE AccountID = @AccountID;

--Do something(s) else?

COMMIT;

然后,当线程 1 仍在执行其工作时,线程 2 将无法为

@NewBalance
赋值。

© www.soinside.com 2019 - 2024. All rights reserved.