В MSSQL есть следующие уровни изоляции данных:
Если с 1 и 2 уровнем все понятно, то с остальными не так все просто.
Рассмотрим уровни изоляции на примере, для этого предположим, у нас есть таблица t в которой есть одна строка:
INSERT INTO t (v) VALUES (1)Repeatable read (повторяемость чтения)
| Транзакция 1 | Транзакция 2 |
|---|---|
|
|
Результат при COMMITTED:
| Транзакция 2 не ждет завершения транзакции 1 |
Результат при REPEATABLE READ:
| Транзакция 2 ждет завершения транзакции 1 |
Serializable (упорядочиваемость) - транзакции полностью изолируются друг от друга, каждая выполняется так, как будто параллельных транзакций не существует
| Транзакция 1 | Транзакция 2 |
|---|---|
|
|
Результат при REPEATABLE READ:
| Транзакция 2 не ждет завершения транзакции 1 |
Результат при SERIALIZABLE:
| Транзакция 2 ждет завершения транзакции 1 |
Подведем итог
| Уровень изоляции: | COMMITTED | REPEATABLE_READ | SERIALIZABLE |
|---|---|---|---|
| Изменения строки таблицы видны в других транзакциях | да | нет | нет |
| Добавление/удаление строк производимых в транзакции, видны в других транзакциях | да | да | нет |
По-умолчанию включен режим read committed (подтвержденное чтение), при котором операции insert, update, delete блокируют затрагиваемые строки в таблице, поэтому операция select встает в очередь ожидания разблокировки (пока ресурс не освободится), но, у операции select есть 2 хинта (указателя), благодаря которым можно из заблокированной таблицы вычитывать строки:
WITH(READPAST) - читать зафиксированные строки (будут игнорироваться строки подвергающиеся добавлению/изменению/удалению в транзакциях сторонних сессий) - блокирует вычитываемые строки (накладывая S блокировку)
WITH(NOLOCK) - читать незафиксированные строки (грязные данные) - не блокирует вычитываемые строки (блокировки не накладываются)
Если нужен WITH(NOLOCK) для всех запросов транзакции, тогда нужно перед началом транзакции указать уровень блокировки READ UNCOMMITTED, например так:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
BEGIN TRAN;
Как блокировать
Существуют следующие виды блокировок:
| Вид | Какие операции блокирует | Что именно блокирует |
| Exclusive lock (X) | Insert, Update, Delete | блокировка страницы или строки |
| Shared lock (S) | Insert, Update, Delete, Select | блокировка страницы или строки |
| Update lock (U) | Insert, Update, Delete | блокировка страницы или строки, которая заблокирована с помощью Shared lock (S) |
| Intent locks (I): | намерение о блокировки таблицы целиком, может быть следующим: | |
| – Intent exclusive (IX) | Insert, Update, Delete | см. выше |
| – Intent shared (IS) | Insert, Update, Delete, Select | см. выше |
| – Intent update (IU) | Insert, Update, Delete | см. выше |
К примеру, чтобы исключить простейший случай дедлока, перед UPDATE нужных ID-строк, хорошей практикой считается - блокировка этих ID-строк с помощью SELECT-a:
SELECT id FROM my_table WITH(UPDLOCK,READPAST) WHERE id IN (1,2,...)
и затем сверить кол-во возвращенных ID-строк (если кол-во возвращенных SELECT-ом строк равно кол-ву ID-строк в операторе IN, то все указанные строки были заблокированы в рамках текущей транзакции - BEGIN TRAN должен быть вызван до SELECT-a блокировки).