SQL 在最大金额上插入竞争条件

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

我们正在创建一个拍卖投标系统。对于产品,如果其金额高于上次出价,我们仅允许插入到 AuctionBid ID 表中。

此查询适用于单个操作。

select MAX(Amount) from dbo.AuctionBid where ProductId = 9   // Amount: 150

但是,在多线程并发环境中,同一产品存在多个拍卖出价,这可能会导致竞争条件。其中一个插入读取 (150) 并将插入例如 170,而同时另一个插入已经插入(例如 190)。

如何在 SQL 中解决这个问题?研究不同类型的锁。如果可能的话,尝试防止全表锁定。

我们的桌子有

拍卖出价 产品ID 金额
1 2(沙发) 175
2 9(电视) 100
3 9(电视) 150

当前使用 Microsoft SQL Server 2019。(T-SQL)

sql sql-server database concurrency azure-sql-database
3个回答
0
投票

这样的事情有用吗?将其放入您的插入过程中,如果提供的

@CurrentBid
小于最高出价,则不会插入。您可以添加额外的处理来大惊小怪,或者在出价未被接受时记录一些内容,但这里的关键点是,这会释放表上的锁,以防止任何其他 SPID 尝试执行相同的操作。

insert into AuctionBid
(
    Bid
)
select
    @CurrentBId
where @CurrentBId > (select max(Bid) from AuctionBId)

-- if you need to handle subsequent logic differently if the bid insert failed, do something like this:
if @@rowcount = 0
begin
    raiserror('Oh noes! ur bid wasn''t accepted!', 0, 1) with nowait
end

我对全局临时表进行了测试,首先插入了出价为 50 的行(作为表的种子)。然后,我打开一个新表,并以 9000 的出价向该表中插入数据,但保持事务打开。最后,我打开了第三个选项卡并尝试插入值 8999,但它挂起,等待我的其他事务完成。这似乎意味着在插入完成时该表被锁定,无法插入。

在另一个事务仍处于打开状态时尝试插入的 SPID 正在尝试获取

LCK_M_S
共享锁,并且在另一个事务提交之前无法获得。

当其他 SPID 拥有锁时,您甚至无法从表中选择

max(Bid)
;正如你所希望的那样。


0
投票

考虑 ff 程序

create or alter procedure dbo.addBid (
   @ProductID int,
   @BidAmount int
)
as
begin
   declare @maxBid int;
   begin transaction
      set @maxBid = (
         select max(Amount)
         from dbo.AuctionBid with (holdlock, updlock)
         where ProductId = 9
      );

     if (@bidAmount > @maxBid)
     begin
        insert into dbo.AuctionBid
           (ProductId, Amount)
        values
           (@ProductID, @BidAmount);
     end
     else
     begin
        print 'Specified bid amount is not larger than current bid and is thus rejected';
     end
   commit
end

您可能还需要一些其他错误处理,但这就是要点。这里的想法是,您以阻止并发进程同时执行此操作的方式获取当前最大出价,并在与该选择相同的事务中执行插入。


0
投票
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
-- Get the current max bid for the product
SELECT MAX(Amount) FROM dbo.AuctionBid WHERE ProductId = 9;
-- Your application checks if the new bid is higher and proceeds to insert
INSERT INTO dbo.AuctionBid (ProductId, Amount) VALUES (9, <new_bid>);
COMMIT TRANSACTION;

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