We are creating an auction bidding system. We only allow insertions into the AuctionBid ID table, for a Product, if its higher amount than the last bid.
This query will work for single operations.
select MAX(Amount) from dbo.AuctionBid where ProductId = 9 // Amount: 150
However, in a multithreading concurrency environment where there are a multiple auction bids on the same product, it can cause race conditions. Where one insert reads (150) and is about to insert eg 170, while another already inserted (eg 190) in the meantime.
How can I resolve this in SQL? Researching different types of locks. Trying to prevent full table lock if possible.
Our table has
| AuctionBidId | ProductId | Amount |
| ------------ | ------------ | ------------ |
| 1 | 2 (Couch) | 175 |
| 2 | 9 (TV) | 100 |
| 3 | 9 (TV) | 150 |
Currently using Microsoft SQL Server 2019. (T-SQL)
3条答案
按热度按时间4nkexdtk1#
Consider the ff procedure
There's probably some other error handling you'd want in there, but this is the gist. The idea here is that you obtain the current max bid in such a way as to preclude concurrent processes from also doing so at the same time and do the insert in the same transaction as that select.
The query hints we're using here are:
HOLDLOCK (Serializable) - "Other transactions cannot insert new rows with key values that would fall in the range of keys read by any statements in the current transaction until the current transaction completes."
UPLOCK - in this case, we are forcing an exclusive lock, since SQL Server can only take one UPLOCK , instead of a shared READLOCK
4xrmg8kj2#
goucqfw63#
Does something like this do the trick? Put this in your insert proc, and if the
@CurrentBid
provided is less than the max bid, there will be no insert. You could add additional handling to make a fuss or log something in the event the bid wasn't accepted, but the key point here is this takes out locks on the table preventing any other SPIDs from trying to do the same.I ran a test on a global temp table where I first inserted a row with a bid of 50 (to seed the table). I then opened a new table and ran an insert into the table with a bid of 9000, but left the transaction open. Finally, I opened a third tab and attempted to insert a value of 8999, and it hung waiting for my other transaction to complete. That would seem to imply the table is locked from inserts while that insert is completing.
The SPID trying to insert while the other transaction is still open is trying to get a
LCK_M_S
shared lock, and can't till the other transaction commits.You can't even select the
max(Bid)
from the table while the other SPID has the lock; as you would hope.