Установка 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;
В другом сеансе вы можете вставить строки в таблицу в точках 1, 2, 3 или 4. Это может показаться хорошим вариантом, за исключением того, что происходит для любой вставки, которая происходит между 2 и 3, это то, что автоматически сгенерированное значение срабатывает другой сеанс основан на результатах оператора 2 - поэтому он сгенерирует 101, а затем оператор 3 потерпит неудачу с нарушением первичного ключа. Это довольно просто настроить и проверить себя с некоторыми WAITFOR
s:
-- 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;
Как только этот пакет запущен, запустите этот пакет в другом окне:
-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20
Сессия 2 должна только когда-либо вставлять значения от 1 до 20, верно? За исключением того, что из-за того, что базовая идентификационная информация была обновлена вашим сеансом 1 вставки вручную, в какой-то момент сеанс 2 выберет, где сеанс 1 закончился, и вставит 32, или 33, или 34 и т. Д. Это будет разрешено сделать, но затем сеанс 1 завершится неудачно при следующей вставке с нарушением PK (выигрыш которого может быть просто вопросом времени).
Один из способов обойти это - вызвать a TABLOCK
при первой вставке:
INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);
Это заблокирует любых других пользователей, пытающихся вставить (или сделать что-нибудь на самом деле) с этой таблицей, пока вы не закончите перемещение этих заархивированных строк назад. Конечно, это ограничивает параллелизм, но именно так вы хотите, чтобы блокировка работала. И, надеюсь, это не то, что происходит так часто, когда вы все время блокируете других людей.
Пара других обходных путей:
- прекратить заботиться о
IDENTITY
сгенерированной стоимости. Какая разница? Используйте UNIQUEIDENTIFIER
(может быть сгенерировано в отдельной таблице с IDENTITY
суррогатом), если исходное значение очень важно.
- измените процесс архивирования, чтобы использовать «мягкое удаление», когда что-то помечается как изначально заархивированное, а архивирование не становится постоянным до более поздней даты. Тогда любой процесс, который пытается переместить их обратно, может просто выполнить прямое обновление и установить флаг мягкого удаления.