Durch das Festlegen IDENTITY_INSERT ON
allein wird die Parallelität nicht beseitigt. Dadurch werden keine exklusiven Sperren für die Tabelle platziert, sondern nur eine kurze Sch-S-Sperre (Schemas).
Was also theoretisch unter dem Standardverhalten passieren könnte, ist, dass Sie dies in Sitzung 1 tun könnten:
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;
In einer anderen Sitzung können Sie an den Punkten 1, 2, 3 oder 4 Zeilen in die Tabelle einfügen. Dies scheint eine gute Sache zu sein, außer dass bei jeder Einfügung zwischen 2 und 3 der automatisch generierte Wert ausgelöst wird Eine andere Sitzung basiert auf den Ergebnissen von Anweisung 2 - es wird also eine 101 generiert, und dann schlägt Anweisung 3 mit einer Primärschlüsselverletzung fehl. Dies ist ziemlich einfach einzurichten und mit einigen WAITFOR
s zu testen :
-- 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;
Sobald dieser Stapel gestartet wurde, starten Sie diesen Stapel in einem anderen Fenster:
-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20
Sitzung 2 sollte immer nur Werte von 1-20 einfügen, oder? Abgesehen davon, dass die zugrunde liegende Identität durch das manuelle Einfügen von Sitzung 1 aktualisiert wurde, wird Sitzung 2 irgendwann dort fortgesetzt, wo Sitzung 1 aufgehört hat, und 32, 33 oder 34 usw. eingefügt. Dies ist jedoch zulässig dann schlägt Sitzung 1 beim nächsten Einfügen mit einer PK-Verletzung fehl (die man gewinnt, kann nur eine Frage des Timings sein).
Eine Möglichkeit, dies zu umgehen, besteht darin, TABLOCK
beim ersten Einfügen ein aufzurufen :
INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);
Dadurch werden alle anderen Benutzer blockiert, die versuchen, diese Tabelle einzufügen (oder wirklich etwas zu tun), bis Sie mit dem Zurückschieben dieser archivierten Zeilen fertig sind. Dies drosselt Gleichzeitigkeit, sicher, aber das ist so , wie Sie wollen , um Arbeit zu blockieren. Und hoffentlich passiert das nicht so häufig, wenn Sie ständig andere Leute blockieren.
Einige andere Problemumgehungen:
- Hör auf, dich um den
IDENTITY
generierten Wert zu kümmern . Wen interessiert das? Verwenden Sie a UNIQUEIDENTIFIER
(möglicherweise in einer separaten Tabelle mit einem IDENTITY
als Ersatz generiert ), wenn der ursprüngliche Wert sehr wichtig ist.
- Ändern Sie den Archivierungsprozess so, dass ein "Soft Delete" verwendet wird, bei dem etwas anfänglich als archiviert markiert ist und die Archivierung erst zu einem späteren Zeitpunkt dauerhaft wird. Dann kann jeder Prozess, der versucht, sie zurückzuschieben, einfach eine direkte Aktualisierung durchführen und das Soft-Delete-Flag korrigieren.