SQL Server同时执行多个插入操作



我遇到了一个问题,即我得到了多个插入的行,其中只有一行应该插入。

我有一个类似的过程,但该过程在同一时刻由不同的源使用相同的输入参数执行多次,因此我们得到了具有相同数据的多个重复行。

编辑:更多信息,我们得到了多个不同的事件,如果每个事件都有一个以前没有添加到表中的新server_id,那么它可能会执行这个过程。有时,我们会得到多个事件,它们都有相同的server_id,但尚未输入数据库,所以它们都试图同时通过此过程将其输入数据库,我们会在表中得到多行

CREATE PROCEDURE [stuff].[p_insert_missing_data]
@in_event_timestamp DATETIME2(7),
@in_source_system   NVARCHAR(50),
@in_server_id       NVARCHAR(50)
AS
BEGIN
INSERT INTO house.servers (
xtract_datetime, 
xtract_source, 
server_id
)
SELECT GETDATE()           AS xtract_datetime,
@in_source_system   AS xtract_source,
@in_server_id       AS server_id
WHERE
NOT EXISTS (SELECT 1 FROM house.servers WHERE server_id = @in_server_id)
END

为了确保不会出现竞争条件,即多个进程试图插入同一行,您需要SERIALIZABLE隔离级别。您可以在整个过程中设置这一点,或者更好的做法是仅向表引用WITH (SERIALIZABLE)WITH (HOLDLOCK)添加显式提示。然后,服务器将放置一个密钥范围锁,该锁不仅锁定该密钥,还锁定密钥的缺失。它还将保持该锁直到事务结束(如果没有事务,则为语句(。

这样做的缺点是有更多的锁定,增加了死锁的风险。因此,您还应该添加一个UPDLOCK提示。

INSERT INTO house.servers (
xtract_datetime, 
xtract_source, 
server_id
)
SELECT GETDATE(),
@in_source_system,
@in_server_id
WHERE
NOT EXISTS (SELECT 1
FROM house.servers s WITH (SERIALIZABLE, UPDLOCK)
WHERE s.server_id = @in_server_id);

您不需要事务,因为每个语句都在自己的事务中隐式运行。

为了只放置一个键锁,而不锁定整个表,还需要一个索引。它可以是集群的,也可以是非集群的,尽管我猜server_id是主键,因此通常应该是集群的。

house.servers (server_id ASC)

最新更新