Неожиданное поведение Сериализуемого уровня изоляции - PullRequest
0 голосов
/ 04 апреля 2020

Настройка теста

У меня есть SQL Server 2014 и простая таблица MyTable, которая содержит столбцы Code (int) и Data (nvarchar(50)), индексы для этой таблицы не созданы.

У меня есть 4 записи в таблице следующим образом:

1, First
2, Second
3, Third
4, Fourth

Затем я выполняю следующий запрос в транзакции:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

BEGIN TRANSACTION

DELETE FROM dbo.MyTable 
WHERE dbo.MyTable.Code = 2

У меня есть одна затронутая row и я не выдаю Commit или Rollback.

Далее я запускаю еще одну транзакцию:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRANSACTION

SELECT TOP 10 Code, Data
  FROM dbo.MyTable
  WHERE Code = 3

На этом шаге транзакция с запросом SELECT зависает в ожидании завершения транзакции. с запросом DELETE.

Мой вопрос

Я не понимаю, почему транзакция с запросом SELECT ожидает транзакцию с запросом DELETE. В моем понимании удаленная строка (с кодом 2) не имеет ничего общего с выбранной строкой (с кодом 3), и, насколько я понимаю, спецификация c уровня изоляции SERIALIZABLE SQL Сервер не должен блокировать всю таблицу в этот случай. Может быть, это происходит потому, что минимальная блокировка для SERIALIZABLE - это страница? Тогда это может привести к непоследовательному поведению при выборе строк на некоторых других страницах, если в таблице будет больше строк, например, 1000000 (некоторые строки с других страниц не будут заблокированы). Пожалуйста, помогите выяснить, почему блокировка происходит в моем случае.

1 Ответ

2 голосов
/ 04 апреля 2020

При блокировке READ COMMITTED, REPEATABLE READ или SERIALIZABLE запрос SELECT должен устанавливать блокировки Shared (S) для каждой строки, которую фактически читает план запроса. Блокировки могут быть размещены на уровне строк, страниц или таблиц. Кроме того, SERIALIZABLE будет устанавливать блокировки на диапазоны, чтобы ни один другой сеанс не мог вставить соответствующую строку, пока блокировка удерживается.

И поскольку у вас "нет индексов, созданных для этой таблицы", этот запрос:

SELECT TOP 10 Code, Data
  FROM dbo.MyTable
  WHERE Code = 3

Должен быть выполнен с помощью сканирования таблицы, и он должен прочитать все строки (даже те, которые имеют код = 2), чтобы определить, соответствуют ли они критериям SELECT.

Это одна из причин, почему вы почти всегда следует использовать Row-Versioning , либо установив для базы данных значение READ COMMITTED SNAPSHOT, либо кодируя транзакции только для чтения для использования изоляции SNAPSHOT.

...