Ещё одна «засада» на уровне изоляции Read Uncommitted

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

По материалам статьи Craig Freedman: Query Failure with Read Uncommitted

Опубликовано 23 марта 2019 г., впервые опубликовано в MSDN 12 июня 2007 г.

В предыдущих статьях были рассмотрены практически все уровни изоляции, за исключением Read Uncommitted или NOLOCK. Эта статья завершает серию обсуждением того, что может приключиться, если читать данные ещё не зафиксированных транзакций. О вреде NOLOCK написано уже немало. Например, вы могли об этом почитать у Любора Коллара (Lubor Kollar) из «SQL Server Development Customer Advisory Team» и в (ныне уже недоступном) блоге Тони Роджерсона (Tony Rogerson).

В дополнение к многочисленным аргументам, ниже будет продемонстрирована еще одна опасность NOLOCK. Начнём с создания двух таблиц:

create table t1 (k int, data int)
insert t1 values (0, 0)
insert t1 values (1, 1)

create table t2 (pk int primary key)
insert t2 values (0)
insert t2 values (1)

Затем в первом сеансе заблокируйте первую строку t2, используя обновление, как показано ниже:

begin tran
update t2 set pk = pk where pk = 0

Теперь во втором сеансе выполните следующий запрос:

select *
from t1 with (NOLOCK)
where exists (select * from t2 where t1.k = t2.pk)

Этот запрос использует следующий план:

|--Nested Loops(Left Semi Join, WHERE:([t1].[k]=[t2].[pk]))
   |--Table Scan(OBJECT:([t1]))
   |--Clustered Index Scan(OBJECT:([t2].[PK__t2__71D1E811]))

Сканирование таблицы выбирает первую строку t1 без получения каких-либо блокировок, а затем пытается соединить эту строку с t2. Поскольку мы заблокировали первую строку в t2 и поскольку сканирование кластерного индекса t2 выполняется на уровне изоляции по умолчанию Read Committed, запрос блокируется.

Наконец, в первом сеансе удалите первую строку из t1 и зафиксируйте транзакцию:

delete t1 where k = 0
commit tran

Запрос во втором сеансе теперь можно продолжить. Однако мы только что удалили ту строку, к которой ему нужно было выполнить соединение, и чего он ждал, пока она была заблокирована. Поскольку запрос пытается получить данные из удаленной строки, он завершается ошибкой со следующим текстом:

Msg 601, Level 12, State 3, Line 1
Could not continue scan with NOLOCK due to data movement.

Как видно, сканирование с Read Uncommitted или сканирование с NOLOCK может привести не только к неверным результатам, но и даже к сбою запроса!

SQL Server 2000 также мог выдавать такую ошибку, если план запроса содержит Bookmark Lookup и строка удалялась после того, как она была возвращена поиском по некластеризованному индексу, но до того, как строка базовой таблицы была выбрана Bookmark Lookup. SQL Server 2005 при таком сценарии не выдает ошибки. Напомним, что в SQL Server 2005 оператор Bookmark Lookup это просто соединение. Таким образом, если Bookmark Lookup не может найти соответствующую строку базовой таблицы, он просто отбрасывает её, как и любое другое соединение.

Источник: https://habr.com/ru/post/585634/


Интересные статьи

Интересные статьи

Количество хорошо подготовленных целевых кибератак на критическую информационную инфраструктуру предприятий и целых городов в разных странах растёт с каждым годом. Причём в некоторых сл...
Я много занимался деконструкцией инди-игры 2017 года Unexplored Джориса Дорманса. Она идеально воплощает идею процедурно генерируемых подземелий в стиле Zelda, и я обязан был выяснить, ...
За несколько дней до начала продаж в базах SiSoftware, GeekBench и UserBenchmark появились бенчмарки 64-ядерного процессора AMD Ryzen Threadripper 3990X. Теперь можно на фактах убедиться, нас...
Если честно, к Д7 у меня несколько неоднозначное отношение. В некоторых местах я попискиваю от восторга, а в некоторых хочется топать ногами и ругаться неприличными словами.
Периодически мне в разных вариантах задают вопрос, который «в среднем» звучит так: «что лучше: заказать интернет-магазин на бесплатной CMS или купить готовое решение на 1С-Битрикс и сделать магазин на...