在我们的一个数据库中,我们有一个由多个线程密集并发访问的表。线程确实通过MERGE. 还有一些线程偶尔会删除行,因此表数据非常不稳定。执行 upsert 的线程有时会陷入死锁。该问题看起来与此问题中描述的问题相似。不过,不同之处在于,在我们的例子中,每个线程都只更新或插入一行。
简化设置如下。该表是堆,上面有两个唯一的非聚集索引
CREATE TABLE [Cache]
(
[UID] uniqueidentifier NOT NULL CONSTRAINT DF_Cache_UID DEFAULT (newid()),
[ItemKey] varchar(200) NOT NULL,
[FileName] nvarchar(255) NOT NULL,
[Expires] datetime2(2) NOT NULL,
CONSTRAINT [PK_Cache] PRIMARY KEY NONCLUSTERED ([UID])
)
GO
CREATE UNIQUE INDEX IX_Cache ON [Cache] ([ItemKey]);
GO
Run Code Online (Sandbox Code Playgroud)
典型的查询是
DECLARE
@itemKey varchar(200) = 'Item_0F3C43A6A6A14255B2EA977EA730EDF2',
@fileName nvarchar(255) = 'File_0F3C43A6A6A14255B2EA977EA730EDF2.dat';
MERGE INTO [Cache] WITH (HOLDLOCK) T
USING (
VALUES (@itemKey, @fileName, dateadd(minute, 10, sysdatetime()))
) S(ItemKey, FileName, Expires)
ON …Run Code Online (Sandbox Code Playgroud) 在我的应用程序中,我必须执行分布式锁定模式。因为我们已经有一个 SQL Server 实例可以使用,所以我们决定在我们的 Web 应用程序的 SQL 层实现锁定是最容易的。
可以根据多种条件获得锁,包括:
出于所有意图和目的,将上述两个条件视为int数据类型。
在这种模式中,我们希望将我们所有的锁都视为 FIFO,我相信SERIALIZABLE隔离级别会给我们带来这种好处。
以下是我们建议如何执行“锁定”:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
IF EXISTS (SELECT 1 FROM locks WHERE LockType = @LockType AND ApplicationIdentifier = @ApplicationIdentifier)
BEGIN
-- Awesome, the lock will be acquired
INSERT INTO locks OUTPUT INSERTED.LockId VALUES (2,3)
END
ELSE
BEGIN
-- Someone already has the lock
SELECT -1
END
SET TRANSACTION ISOLATION LEVEL READ COMITTED
Run Code Online (Sandbox Code Playgroud)
和“解锁”:
DELETE FROM locks WHERE LockId = @LockId
Run Code Online (Sandbox Code Playgroud)
所以我的问题有两个方面: …
在表中Orders,我存储了我们从所有商店收到的订单。由于一个订单可以有多个行,列中有OrderID和OrderLineID
在那里OrderID可以复制,但OrderLineID必须是一个顺序中是唯一的。
由于订单可以修改,存储过程首先检查收到的订单OrderLineID是否已经存在于表中,然后决定插入还是更新。为此,我们:
然后是主表:
IF NOT EXISTS (Select 1 from Orders where OrderLineID=@OrderLineID ......)
INSERT INTO Orders () VALUES ()
ELSE UPDATE Orders SET ... WHERE OrderLineID=@OrderLineID
Run Code Online (Sandbox Code Playgroud)
或者该MERGE功能是否提供更好的性能/控制?
但问题如下:
由于线路问题/服务器繁忙等,Order消息(或修改)可能会被多次发送,我们不知道按哪个顺序发送。因此,为了避免Order修改后到达,从而覆盖修改,我们添加了一个时间列:
IF NOT EXISTS (Select 1 from Orders where OrderLineID=@OrderLineID)
INSERT INTO Orders () VALUES ()
ELSE UPDATE Orders SET ... WHERE OrderLineID=@OrderLineID AND LastModified<@CreatedTime
Run Code Online (Sandbox Code Playgroud)
这样,如果后一条消息比前一条消息旧,则对表没有影响。
但是,消息及其修改可能会在很短的时间内发送两次(或更多),以至于后一条消息在保存前一条消息之前到达。因此,对于存储过程的两次执行,它 …