Взаимоблокировка

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

  • Транзакция А создает общую блокировку строки 1.

  • Транзакция Б создает общую блокировку строки 2.

  • Транзакция А теперь запрашивает монопольную блокировку строки 2 и блокируется до того, как транзакция Б закончится и освободит общую блокировку строки 2.

  • Транзакция Б теперь запрашивает монопольную блокировку строки 1 и блокируется до того, как транзакция A закончится и освободит общую блокировку строки 1.

Транзакция А не может завершиться до того, как завершится транзакция Б, а транзакция Б заблокирована транзакцией А. Такое условие также называется цикличной зависимостью: транзакция А зависит от транзакции Б, а транзакция Б зависит от транзакции А и этим замыкает цикл.

Обе транзакции находятся в состоянии взаимоблокировки и будут всегда находиться в состоянии ожидания, если взаимоблокировка не будет разрушена внешним процессом. Монитор взаимоблокировок компонента MicrosoftSQL Server Database Engine периодически проверяет задачи на состояние взаимоблокировки. Если монитор обнаруживает цикличную зависимость, то выбирается одна задача, для которой транзакция будет завершена с ошибкой. Это позволяет другой задаче завершить свою транзакцию. Позднее приложение может повторно выполнить транзакцию, которая завершилась с ошибкой, обычно после того как другая транзакция (бывшая в состоянии взаимоблокировки) завершится.

Использование определенных соглашений по написанию кода приложений уменьшает вероятность того, что приложение вызовет взаимоблокировку. Дополнительные сведения см. в разделе Минимизация взаимоблокировок.

Взаимоблокировки часто путают с обычными блокировками. Если транзакция запрашивает блокировку на ресурс, заблокированный дугой транзакцией, то запрашивающая транзакция ожидает до тех пор, пока блокировка не освобождается. По умолчанию время ожидания транзакций сервера SQL Server не ограничено, если только не установлен параметр LOCK_TIMEOUT. Запрашивающая транзакция блокируется, но не устанавливается в состояние взаимоблокировки, потому что запрашивающая транзакция ничего не сделала, чтобы заблокировать транзакцию, владеющую блокировкой. Наконец, владеющая транзакция завершится и освободит блокировку, и затем запрашивающая транзакция получит блокировку и продолжится.

Взаимоблокировки иногда называют тупиковыми ситуациями.

Условие взаимоблокировки может возникнуть в любой системе с несколькими потоками, не только в системе управления реляционными базами данных, и может возникнуть для ресурсов, отличных от блокировок объектов баз данных. Например, в многопоточной операционной системе один поток может занять один или более ресурсов, таких как блокировки памяти. Если в настоящее время другой поток владеет занятым ресурсом, то первый поток должен ждать, пока владеющий поток освободит целевой ресурс. В таком случае говорят, что ожидающий поток зависит от владеющего потока для данного ресурса. В экземпляре компонента Database Engine сеансы оказываются взаимозаблокированными при использовании ресурсов, отличных от баз данных, таких как память или потоки.

Диаграмма, иллюстрирующая взаимоблокировку транзакций

На рисунке транзакция Т1 зависит от транзакции Т2 для ресурса блокировки таблицы Деталь. Аналогично транзакция Т2 зависит от транзакции Т1 для ресурса блокировки таблицы Поставщик. Так как эти зависимости из одного цикла, возникает взаимоблокировка транзакций T1 и T2.

Взаимоблокировка может произойти также в случае, когда таблица секционирована, а параметр LOCK_ESCALATION инструкции ALTER TABLE имеет значение AUTO. Если параметр LOCK_ESCALATION имеет значение AUTO, то степень параллелизма можно повысить, разрешив компоненту Database Engine блокировать секции таблиц на уровне HoBT, а не TABLE. Однако если отдельные транзакции удерживают блокировки секций в таблице и пытаются заблокировать еще какой-либо объект в разделе, принадлежащем другой транзакции, это вызовет взаимоблокировку. Такого типа взаимоблокировок можно избежать, установив параметр LOCK_ESCALATION в значение TABLE. Однако это заметно снизит степень параллелизма, поскольку операциям массового обновления данных секции нужно будет ожидать блокировки таблицы.