Простая лабораторная работа для демонстрации опасности NOLOCK в операторах INSERT
Пересказ статьи William Assaf. A simple lab to demonstrate the danger of NOLOCKs in INSERT statements
Уровню изоляции READ UNCOMMITTED, который активирует табличный хинт NOLOCK, не следует доверять, когда он используется при записи данных. Вы не всегда защищены ошибкой 1065 в случаях, когда запись сталкивается с NOLOCK.
Ошибка 1065 говорит: "Хинты NOLOCK и READUNCOMMITTED не разрешаются для таблиц при операторах INSERT, UPDATE, DELETE или MERGE". Однако NOLOCK может все же представлять опасность для источника записи. (NOLOCK также может завершиться с ошибкой 601 даже в базовых операторах SELECT, но программистов это не всегда пугает.)
Вот самый элементарный пример, которым можно поделиться на вечеринках, чтобы отпугнуть разработчиков от авантюрного использования NOLOCK:
Итак, в таблице у нас имеется одна строка. Давайте начнем транзакцию для вставки другой строки, но не фиксируем её.
А для второй таблицы давайте выполним INSERT на базе оператора SELECT с табличным хинтом NOLOCK.
Без хинта NOLOCK это будет блокироваться незафиксированным оператором INSERT в подключении 1.
С хинтом NOLOCK оператор INSERT во втором подключении сразу выполняется... однако вернемся к подключению 1:
Что мы теперь имеем? На любом подключении:
В то время когда INSERT в testnolock1 откатывается (по одной из многих возможных ошибок или сбоев, не обязательно из-за ROLLBACK), INSERT с обреченной строкой, у которой text1=2, отлично отработал в testnolock2. Это плохо! Если на соединении 1 повторят свой INSERT, мы получили бы дублирующиеся данные, нарушение ограничения первичного ключа и т.п.
Если это кажется вам элементарным, отлично! У вас есть здоровое понимание и страх перед хаосом, который NOLOCK может посеять.
Вот самый элементарный пример, которым можно поделиться на вечеринках, чтобы отпугнуть разработчиков от авантюрного использования NOLOCK:
-- Соединение 1
DROP TABLE IF EXISTS dbo.testnolock1
GO
CREATE TABLE dbo.testnolock1
(id int not null IDENTITY (1,1) PRIMARY KEY,
text1 varchar(10) not null
)
GO
INSERT INTO dbo.testnolock1 (text1) VALUES (1);
GO
Итак, в таблице у нас имеется одна строка. Давайте начнем транзакцию для вставки другой строки, но не фиксируем её.
-- Соединение 1
BEGIN TRAN
INSERT INTO dbo.testnolock1 (text1) VALUES (2);
А для второй таблицы давайте выполним INSERT на базе оператора SELECT с табличным хинтом NOLOCK.
-- Соединение 2
DROP TABLE IF EXISTS dbo.testnolock2
GO
CREATE TABLE dbo.testnolock2
(id int not null IDENTITY (1,1) PRIMARY KEY,
text1 varchar(10) not null
)
GO
INSERT INTO testnolock2
SELECT TEXT1 FROM testnolock1 (NOLOCK)
Без хинта NOLOCK это будет блокироваться незафиксированным оператором INSERT в подключении 1.
С хинтом NOLOCK оператор INSERT во втором подключении сразу выполняется... однако вернемся к подключению 1:
-- Подключение 1
ROLLBACK TRAN
Что мы теперь имеем? На любом подключении:
SELECT * FROM testnolock1;
SELECT * FROM testnolock2;
В то время когда INSERT в testnolock1 откатывается (по одной из многих возможных ошибок или сбоев, не обязательно из-за ROLLBACK), INSERT с обреченной строкой, у которой text1=2, отлично отработал в testnolock2. Это плохо! Если на соединении 1 повторят свой INSERT, мы получили бы дублирующиеся данные, нарушение ограничения первичного ключа и т.п.
Если это кажется вам элементарным, отлично! У вас есть здоровое понимание и страх перед хаосом, который NOLOCK может посеять.
Обратные ссылки
Автор не разрешил комментировать эту запись
Комментарии
Показывать комментарии Как список | Древовидной структурой