IDENTITY_INSERT 如何影响并发?

Met*_*hor 12 sql-server-2005 sql-server insert identity

我正在尝试使用 3rd 方 SAP 附加组件帮助客户,该附加组件有发布故障并且已停止支持。

在某些情况下,它会将发帖队列表中未完成的帖子归档到发帖存档表。我需要将这些存档的结果移回队列中。

队列 ID 是一个身份列,我想保持不变。

问题是,如果我执行 identity_insert on/insert/identity_insert off,对于创建队列条目并期望自动生成标识列的进程的并发性,我可以期待什么?

任何有关演示此类行为的最佳方式的指针也将不胜感激。

Aar*_*and 9

IDENTITY_INSERT ON单独设置不会消除并发性 - 这不会在表上放置任何排他锁,只是一个简短的模式稳定性 (Sch-S) 锁。

那么理论上会发生什么,在默认行为下,您可以在会话 1 中执行此操作:

BEGIN TRANSACTION;

-- 1
SET IDENTITY_INSERT dbo.tablename ON;

-- 2
INSERT dbo.tablename(id, etc) VALUES(100, 'foo'); -- next identity is now 101

-- 3
INSERT dbo.tablename(id, etc) VALUES(101, 'foo'); -- next identity is now 102

-- 4
SET IDENTITY_INSERT dbo.tablename OFF;

COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)

在另一个会话中,您可以在点 1、2、3 或 4 处向表中插入行。这似乎是一件好事,除了在 2 和 3 之间发生的任何插入会触发自动生成的值另一个会话基于语句 2 的结果 - 因此它将生成 101,然后语句 3 将因主键冲突而失败。使用一些WAITFORs设置和测试自己非常简单:

-- session 1
-- DROP TABLE dbo.what;
CREATE TABLE dbo.what(id INT IDENTITY PRIMARY KEY);
GO
BEGIN TRANSACTION;

SET IDENTITY_INSERT dbo.what ON;

INSERT dbo.what(id) VALUES(32);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(33);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(34);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(35);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(36);

SET IDENTITY_INSERT dbo.what OFF;

COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)

该批次开始后,在另一个窗口中启动该批次:

-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20
Run Code Online (Sandbox Code Playgroud)

会话 2 应该只插入 1-20 的值,对吗?除了因为您的手动插入会话 1 已更新了底层身份之外,会话 2 将在某个时候从会话 1 停止的地方开始,并插入 32、33 或 34 等。它将被允许这样做,但是然后会话 1 将在下一次插入时失败,并出现 PK 违规(哪个获胜可能只是时间问题)。

解决此问题的一种方法是TABLOCK在第一次插入时调用 a :

INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);
Run Code Online (Sandbox Code Playgroud)

这将阻止任何其他用户尝试使用此表插入(或执行任何操作),直到您完成将这些归档行移回。这当然会限制并发性,但这是您希望阻塞工作的方式。希望这不会以如此频繁的速度发生,您一直在阻止其他人。

一些其他解决方法:

  • 不再关心IDENTITY产生的价值。谁在乎?如果原始值非常重要,则使用 a UNIQUEIDENTIFIER(可能在单独的表中生成并IDENTITY作为代理)。
  • 更改存档过程以使用“软删除”,其中最初将某些内容标记为已存档,并且存档直到稍后日期才会永久化。然后任何试图将它们移回的进程都可以简单地执行直接更新并修复软删除标志。