Обнаружение и устранение взаимоблокировок

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

  • Задача T1 блокирует ресурс R1 (изображается в виде стрелки от R1 к T1) и запросила блокировку ресурса R2 (изображается в виде стрелки от T1 к R2).

  • Задача T2 блокирует ресурс R2 (изображается в виде стрелки от R2 к T2) и запросила блокировку ресурса R1 (изображается в виде стрелки от T2 к R1).

  • Так как ни одна из задач не может продолжиться до тех пор, пока не освободится ресурс, а ни один из ресурсов не может быть освобожден до тех пор, пока не продолжится задание, существует состояние взаимоблокировки.

Диаграмма, показывающая задачи в состоянии взаимоблокировки

Компонент SQL Server Database Engine автоматически обнаруживает цикл взаимоблокировки в SQL Server. Компонент Database Engine для устранения взаимоблокировки выбирает один из сеансов в качестве жертвы взаимоблокировки и прекращает выполнение текущей транзакции с ошибкой.

Ресурсы, которые могут принимать участие во взаимоблокировке

Каждый сеанс пользователя может иметь одну или несколько запущенных от его имени задач, которые могут использовать или ожидать использование различных ресурсов. Следующие типы ресурсов могут привести к блокировке, которая может привести к взаимоблокировке.

  • Блокировки Ожидание применения блокировки такого ресурса, как объект, страница, строка, метаданные и приложение, может привести к взаимоблокировке. Например, транзакция T1 применила общую (S) блокировку строки r1 и ожидает монопольную (X) блокировку строки r2. Транзакция T2 применила общую (S) блокировку строки r2 и ожидает монопольную (X) блокировку строки r1. В результате получается цикл блокировки, в котором T1 и T2 ожидают, пока одна транзакция освободит заблокированный другой транзакцией ресурс.

  • Рабочие потоки Задача, ожидающая в очереди доступного рабочего потока, может привести к взаимоблокировке. Если задача, ожидающая в очереди, владеет ресурсами, которые блокируют все рабочие потоки, результатом будет взаимоблокировка. Например, сеанс S1 запускает транзакцию и применяет общую (S) блокировку строки r1, а затем уходит в спящий режим. Активные сеансы, запущенные на всех доступных рабочих потоках, делают попытки применить монопольную блокировку (X) строки r1. Так как сеанс S1 не может использовать рабочий поток, он не может зафиксировать транзакцию и освободить строку r1. Возникает взаимоблокировка.

  • Память. Если параллельные запросы ожидают предоставления памяти, которая не может быть выделена при доступном объеме памяти, может возникнуть взаимоблокировка. Например, два параллельных запроса Q1 и Q2 выполняются как определяемые пользователем функции, использующие соответственно 10 МБ и 20 МБ памяти. Если каждому запросу нужно 30 МБ, а общий доступный объем памяти равен 20 МБ, то Q1 и Q2 должны ожидать, пока один из них не освободит память, то есть возникает взаимоблокировка.

  • Ресурсы, связанные с параллельным выполнением запросов Потоки-координаторы, производители и потребители, ассоциированные с портом обмена, могут заблокировать друг друга, приводя к взаимоблокировке, как правило, при включении, по крайней мере, еще одного процесса, который не является частью параллельного запроса. Кроме того, когда начинается выполнение параллельного запроса, SQL Server определяет степень параллелизма, или число потоков исполнителя, на основе текущей рабочей нагрузки. При неожиданном изменении системной рабочей нагрузки, например когда начинается выполнение на сервере новых запросов или системе не хватает потоков исполнителя, может возникать взаимоблокировка.

  • Ресурсы режима MARS. Эти ресурсы используются для управления чередованием активных запросов в режиме MARS (см. раздел Среда выполнения пакетов и режим MARS).

    • Пользовательские ресурсы Если поток ожидает ресурс, потенциально контролируемый пользовательским приложением, ресурс считается внешним или пользовательским и рассматривается как заблокированный.

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

    • Объект взаимного исключения транзакции Все задачи, выполняемые в одной транзакции, чередуются. Это означает, что только одна задача транзакции может выполняться в данный момент времени. Перед тем как задача может быть запущена на выполнение, она должна получить монопольный доступ к объекту взаимного исключения транзакции.

    Чтобы задача могла быть запущена в режиме MARS, она должна занять объект взаимного исключения сеанса. Если задача выполняется в транзакции, она должна занять объект взаимного исключения транзакции. Этим гарантируется то, что только одна задача будет активна в каждый момент времени данного сеанса и данной транзакции. Как только потребуются необходимые объекты взаимного исключения, задача сможет выполняться. По завершении задачи или завершении посреди запроса сначала освобождается объект взаимного исключения транзакции, затем объект взаимного исключения сеанса в порядке, обратном тому, в котором они занимались. Однако взаимоблокировки могут произойти и с этими ресурсами. В следующем примере кода две задачи, запросы пользователя U1 и U2, выполняются в одном и том же сеансе.

    U1:    Rs1=Command1.Execute("insert sometable EXEC usp_someproc");
    U2:    Rs2=Command2.Execute("select colA from sometable");
    

    Хранимая процедура, выполняемая запросом пользователя U1, заняла объект взаимного исключения сеанса. Если для выполнения хранимой процедуры необходимо длительное время, компонент Database Engine предполагает, что хранимая процедура ждет указаний пользователя. Запрос пользователя U2 ожидает освобождения объекта взаимного исключения сеанса, в то время как пользователь ожидает результирующий набор от U2, а U1 ожидает пользовательский ресурс. Это состояние взаимоблокировки логически представляется так:

Блок-диаграмма, показывающая взаимоблокировку пользовательских процессов.

Обнаружение взаимоблокировки

Все ресурсы, перечисленные в предыдущем разделе, принимают участие в схеме обнаружения взаимоблокировок компонента Database Engine. Обнаружение взаимоблокировки выполняется потоком монитора блокировок, который периодически производит поиск по всем задачам в экземпляре компонента Database Engine. Следующие пункты описывают процесс поиска:

  • Значение интервала по умолчанию составляет 5 секунд.

  • Если поток монитора блокировки находит взаимоблокировки, интервал обнаружения взаимоблокировок снижается с 5 секунд до 100 миллисекунд в зависимости от частоты взаимоблокировок.

  • Если поток монитора блокировки прекращает поиск взаимоблокировок, компонент Database Engine увеличивает интервал до 5 секунд.

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

Компонент Database Engine обычно выполняет только периодическое обнаружение взаимоблокировок. Так как число взаимоблокировок, произошедших в системе, обычно мало, периодическое обнаружение взаимоблокировок помогает сократить издержки от взаимоблокировок в системе.

Если монитор блокировок запускает поиск взаимоблокировок для определенного потока, он идентифицирует ресурс, ожидаемый потоком. После этого монитор блокировок находит владельцев определенного ресурса и рекурсивно продолжает поиск взаимоблокировок для этих потоков до тех пор, пока не найдет цикл. Цикл, определенный таким способом, формирует взаимоблокировку.

После обнаружения взаимоблокировки компонент Database Engine завершает взаимоблокировку, выбрав один из потоков в качестве жертвы взаимоблокировки. Компонент Database Engine прерывает выполняемый в данный момент пакет потока, производит откат транзакции жертвы взаимоблокировки и возвращает приложению ошибку 1205. Откат транзакции жертвы взаимоблокировки снимает все блокировки, удерживаемые транзакцией. Это позволяет транзакциям потоков разблокироваться и продолжить выполнение. Ошибка 1205 жертвы взаимоблокировки записывает в журнал ошибок сведения обо всех потоках и ресурсах, затронутых взаимоблокировкой.

По умолчанию компонент Database Engine выбирает в качестве жертвы взаимоблокировки сеанс, выполняющий ту транзакцию, откат которой потребует меньше всего затрат. В качестве альтернативы пользователь может указать приоритет сеансов в ситуации взаимоблокировки, используя инструкцию SET DEADLOCK_PRIORITY. DEADLOCK_PRIORITY может принимать значения LOW, NORMAL или HIGH или в качестве альтернативы может принять любое целочисленное значение в промежутке (-10 до 10). Приоритет в случае взаимоблокировки по умолчанию устанавливается на значение NORMAL. Если у двух сеансов имеются различные приоритеты в случае взаимоблокировки, то в качестве жертвы взаимоблокировки будет выбран сеанс с более низким приоритетом. Если у обоих сеансов установлен одинаковый приоритет в случае взаимоблокировки, то в качестве объекта взаимоблокировки будет выбран сеанс, откат которого потребует наименьших затрат. Если сеансы, вовлеченные в цикл взаимоблокировки, имеют один и тот же приоритет в случае взаимоблокировки и одинаковую стоимость, то жертва взаимоблокировки выбирается случайным образом.

При работе со средой CLR монитор взаимоблокировки автоматически обнаруживает взаимоблокировку для ресурсов синхронизации (мониторы, блокировки чтения и записи и соединение потоков), доступ к которым был получен изнутри управляемых процедур. Однако взаимоблокировка снимается путем создания сообщения об исключительной ситуации в процедуре, которая была выбрана в качестве жертвы взаимоблокировки. Важно понимать, что исключение не освобождает ресурсы, которыми владеет жертва взаимоблокировки, автоматически; ресурсы должны быть освобождены явно. В соответствии с поведением исключения, исключение, используемое для идентификации жертвы взаимоблокировки, может быть поймано и отклонено.

Информационные средства взаимоблокировок

Для просмотра сведений о взаимоблокировках компонент Database Engine предлагает средство мониторинга в форме двух флагов трассировки и события Deadlock Graph в Приложение SQL Server Profiler.

Флаги трассировки 1204 и 1222

При возникновении взаимоблокировок флаги трассировки 1204 и 1222 возвращают сведения, фиксируемые в журнале ошибок SQL Server 2005. Флаг трассировки 1204 сообщает сведения о взаимоблокировках, отформатированные каждым узлом, принимающим участие во взаимоблокировке. Флаг трассировки 1222 форматирует сведения о взаимоблокировках сначала по процессам, а затем по ресурсам. Есть возможность включения обоих флагов трассировки для получения двух представлений одного события взаимоблокировки.

Помимо указания свойств флагов трассировки 1204 и 1222, в таблице ниже содержатся их сходства и различия.

Свойство

Флаги трассировки 1204 и 1222

Только флаг трассировки 1204

Только флаг трассировки 1222

Формат вывода

Результаты фиксируются в журнале ошибок SQL Server 2005.

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

Возвращает сведения в XML-формате, не соответствующем определению схемы XML (XSD). В формате предусмотрено три основных раздела. В первом разделе объявляется пострадавший в результате взаимоблокировки объект. Во втором разделе описываются все процессы, вовлеченные во взаимоблокировку. В третьем разделе приводятся ресурсы, синонимичные узлам во флаге трассировки 1204.

Идентифицирующие атрибуты

SPID:<x> ECID:<x>. Определяет поток идентификатора системных процессов в случае параллельной обработки. Запись SPID:<x> ECID:0, где <x> заменено значением SPID, представляет основной поток. Запись SPID:<x> ECID:<y>, где <x> заменено значением SPID и <y> больше 0, представляет субпотоки одного SPID.

BatchID (sbid для флага трассировки 1222). Определяет пакет, из которого выполнение кода запрашивает или удерживает блокировку. Если режим MARS отключен, значение BatchID равно 0. Если режим MARS включен, для активных пакетов задается значение в диапазоне от 1 до n. При отсутствии активных пакетов в сеансе BatchID присваивается значение 0.

Mode. Задает тип блокировки для конкретного ресурса, который запрошен, предоставлен или ожидается потоком. Значением Mode может быть IS (с намерением совмещаемого доступа), S (совмещаемая), U (на обновление), IX (с намерением монопольного доступа), SIX (совмещаемая с намерением монопольного доступа) и X (монопольная). Дополнительные сведения см. в разделе Режимы блокировки.

Line # (line для флага трассировки 1222). Содержит номер строки в текущем пакете инструкций, который выполнялся в момент возникновения взаимоблокировки.

Input Buf (inputbuf для флага трассировки 1222). Выводит все инструкции в текущем пакете.

Node. Представляет номер записи в цепочке взаимоблокировки.

Lists. Владелец блокировки может быть частью этих списков:

  • Grant List. Перечисляет текущих владельцев ресурса.

  • Convert List. Перечисляет текущих владельцев, которые пытаются перенести блокировки на более высокий уровень.

  • Wait List. Перечисляет текущие запросы на новые блокировки ресурса.

Statement Type. Описывает тип инструкции DML (SELECT, INSERT, UPDATE или DELETE), для которой потокам выданы разрешения.

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

Next Branch. Представляет два или более субпотока из одного SPID, которые участвуют в цикле взаимоблокировки.

deadlock victim. Представляет собой адрес физической памяти задачи (см. раздел sys.dm_os_tasks (Transact-SQL)), которая была выбрана в качестве жертвы взаимоблокировки. Может быть равен 0 (нулю) в случае неустраненной взаимоблокировки. Откатываемая задача не может быть выбрана в качестве жертвы взаимоблокировки.

executionstack. Представляет код Transact-SQL, выполняющийся в момент возникновения взаимоблокировки.

priority Представляет собой приоритет в случае взаимоблокировки. В определенных случаях компонент Database Engine может отдать предпочтение изменению приоритета в случае взаимоблокировки на некоторое короткое время для достижения лучшего параллелизма.

logused Пространство журнала, используемое задачей.

owner id. Идентификатор транзакции, которая управляет запросом.

status. Состояние задачи. Принимает одно из следующих значений:

  • pending. Ожидание потока исполнителя.

  • runnable. Готов к запуску, но ожидает такт.

  • running. Выполняется в данный момент в планировщике.

  • suspended. Выполнение приостановлено.

  • done. Задача выполнена.

  • spinloop. Ожидание освобождение элемента Spinlock.

waitresource Ресурс, необходимый для выполнения задачи.

waittime. Время ожидания ресурса в миллисекундах.

schedulerid Планировщик, ассоциированный с этой задачей. См. раздел sys.dm_os_schedulers (Transact-SQL).

hostname. Имя рабочей станции.

isolationlevel. Текущий уровень изоляции транзакции.

Xactid. Идентификатор транзакции, которая управляет запросом.

currentdb. Идентификатор базы данных.

lastbatchstarted Последний раз, когда клиентский процесс запустил выполнение пакета.

lastbatchcompleted Последний раз, когда клиентский процесс завершил выполнение пакета.

clientoption1 и clientoption2 Устанавливает параметры для данного клиентского соединения. Это битовая маска, которая включает сведения о параметрах, обычно управляемых инструкциями SET, такими как SET NOCOUNT и SET XACTABORT.

associatedObjectId Представляет собой идентификатор HoBT (КиСД — куча или сбалансированное дерево).

Атрибуты ресурсов

RID. Определяет одну строку в таблице, по которой удерживается или запрошена блокировка. RID представляется как RID: db_id:file_id:page_no:row_no. Например, RID: 6:1:20789:0.

OBJECT. Определяет таблицу, по которой удерживается или запрошена блокировка. OBJECT представляется как OBJECT: db_id:object_id. Например, TAB: 6:2009058193.

KEY. Определяет диапазон ключа в индексе, по которому удерживается или запрошена блокировка. KEY представляется как KEY: db_id:hobt_id (index key hash value). Например, KEY: 6:72057594057457664 (350007a4d329).

PAG. Определяет страничный ресурс, по которому удерживается или запрошена блокировка. PAG представляется как PAG: db_id:file_id:page_no. Например, PAG: 6:1:20789.

EXT. Определяет структуру экстента. EXT представляется как EXT: db_id:file_id:extent_no. Например, EXT: 6:1:9.

DB. Определяет блокировку базы данных. DB представляется одним из следующих способов:

  • DB: db_id

  • DB: db_id[BULK-OP-DB], который идентифицирует блокировку, выполненную резервной базой данных.

  • DB: db_id[BULK-OP-DB], который идентифицирует блокировку, выполненную журналом резервных копий этой базы данных.

APP. Определяет блокировку, выполненную ресурсом приложения. APP представляется как APP: lock_resource. Например, APP: Formf370f478.

METADATA. Представляет ресурсы метаданных, участвующие во взаимоблокировке. Поскольку METADATA содержит множество вспомогательных ресурсов, возвращаемое значение зависит от заблокированного вспомогательного ресурса. Например, METADATA.USER_TYPE возвращает user_type_id = <integer_value>. Дополнительные сведения о ресурсах и вспомогательных ресурсах METADATA см. в разделе sys.dm_tran_locks (Transact-SQL).

HOBT. Представляет кучу или сбалансированное дерево, участвующее во взаимоблокировке.

Немонопольно для этого флага трассировки.

Немонопольно для этого флага трассировки.

Пример флага трассировки 1204

Следующий пример демонстрирует результаты, выводимые при включенном флаге трассировки 1204. В этом случае таблица в узле 1 — это куча без индексов, а таблица в узле 2 — это куча с некластеризованным индексом. В момент возникновения взаимоблокировки обновляется ключ индекса в узле 2.

Deadlock encountered .... Printing deadlock information
Wait-for graph

Node:1

RID: 6:1:20789:0               CleanCnt:3 Mode:X Flags: 0x2
 Grant List 0:
   Owner:0x0315D6A0 Mode: X        
     Flg:0x0 Ref:0 Life:02000000 SPID:55 ECID:0 XactLockInfo: 0x04D9E27C
   SPID: 55 ECID: 0 Statement Type: UPDATE Line #: 6
   Input Buf: Language Event: 
BEGIN TRANSACTION
   EXEC usp_p2
 Requested By: 
   ResType:LockOwner Stype:'OR'Xdes:0x03A3DAD0 
     Mode: U SPID:54 BatchID:0 ECID:0 TaskProxy:(0x04976374) Value:0x315d200 Cost:(0/868)

Node:2

KEY: 6:72057594057457664 (350007a4d329) CleanCnt:2 Mode:X Flags: 0x0
 Grant List 0:
   Owner:0x0315D140 Mode: X        
     Flg:0x0 Ref:0 Life:02000000 SPID:54 ECID:0 XactLockInfo: 0x03A3DAF4
   SPID: 54 ECID: 0 Statement Type: UPDATE Line #: 6
   Input Buf: Language Event: 
     BEGIN TRANSACTION
       EXEC usp_p1
 Requested By: 
   ResType:LockOwner Stype:'OR'Xdes:0x04D9E258 
     Mode: U SPID:55 BatchID:0 ECID:0 TaskProxy:(0x0475E374) Value:0x315d4a0 Cost:(0/380)

Victim Resource Owner:
 ResType:LockOwner Stype:'OR'Xdes:0x04D9E258 
     Mode: U SPID:55 BatchID:0 ECID:0 TaskProxy:(0x0475E374) Value:0x315d4a0 Cost:(0/380)

Пример флага трассировки 1222

Следующий пример демонстрирует результаты, выводимые при включенном флаге трассировки 1222. В этом случае одна таблица — это куча без индексов, а другая — это куча с некластеризованным индексом. В момент возникновения взаимоблокировки обновляется ключ индекса во второй таблице.

deadlock-list
 deadlock victim=process689978
  process-list
   process id=process6891f8 taskpriority=0 logused=868 
   waitresource=RID: 6:1:20789:0 waittime=1359 ownerId=310444 
   transactionname=user_transaction 
   lasttranstarted=2005-09-05T11:22:42.733 XDES=0x3a3dad0 
   lockMode=U schedulerid=1 kpid=1952 status=suspended spid=54 
   sbid=0 ecid=0 priority=0 transcount=2 
   lastbatchstarted=2005-09-05T11:22:42.733 
   lastbatchcompleted=2005-09-05T11:22:42.733 
   clientapp=Microsoft SQL Server Management Studio - Query 
   hostname=TEST_SERVER hostpid=2216 loginname=DOMAIN\user 
   isolationlevel=read committed (2) xactid=310444 currentdb=6 
   lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
    executionStack
     frame procname=AdventureWorks2008R2.dbo.usp_p1 line=6 stmtstart=202 
     sqlhandle=0x0300060013e6446b027cbb00c69600000100000000000000
     UPDATE T2 SET COL1 = 3 WHERE COL1 = 1;     
     frame procname=adhoc line=3 stmtstart=44 
     sqlhandle=0x01000600856aa70f503b8104000000000000000000000000
     EXEC usp_p1     
    inputbuf
      BEGIN TRANSACTION
       EXEC usp_p1
   process id=process689978 taskpriority=0 logused=380 
   waitresource=KEY: 6:72057594057457664 (350007a4d329)   
   waittime=5015 ownerId=310462 transactionname=user_transaction 
   lasttranstarted=2005-09-05T11:22:44.077 XDES=0x4d9e258 lockMode=U 
   schedulerid=1 kpid=3024 status=suspended spid=55 sbid=0 ecid=0 
   priority=0 transcount=2 lastbatchstarted=2005-09-05T11:22:44.077 
   lastbatchcompleted=2005-09-05T11:22:44.077 
   clientapp=Microsoft SQL Server Management Studio - Query 
   hostname=TEST_SERVER hostpid=2216 loginname=DOMAIN\user 
   isolationlevel=read committed (2) xactid=310462 currentdb=6 
   lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
    executionStack
     frame procname=AdventureWorks2008R2.dbo.usp_p2 line=6 stmtstart=200 
     sqlhandle=0x030006004c0a396c027cbb00c69600000100000000000000
     UPDATE T1 SET COL1 = 4 WHERE COL1 = 1;     
     frame procname=adhoc line=3 stmtstart=44 
     sqlhandle=0x01000600d688e709b85f8904000000000000000000000000
     EXEC usp_p2     
    inputbuf
      BEGIN TRANSACTION
        EXEC usp_p2    
  resource-list
   ridlock fileid=1 pageid=20789 dbid=6 objectname=AdventureWorks2008R2.dbo.T2 
   id=lock3136940 mode=X associatedObjectId=72057594057392128
    owner-list
     owner id=process689978 mode=X
    waiter-list
     waiter id=process6891f8 mode=U requestType=wait
   keylock hobtid=72057594057457664 dbid=6 objectname=AdventureWorks2008R2.dbo.T1 
   indexname=nci_T1_COL1 id=lock3136fc0 mode=X 
   associatedObjectId=72057594057457664
    owner-list
     owner id=process6891f8 mode=X
    waiter-list
     waiter id=process689978 mode=U requestType=wait

Событие Deadlock Graph компонента Profiler

Событие в приложении Приложение SQL Server Profiler, которое представляет собой графическое описание задач и ресурсов, вовлеченных во взаимоблокировку. Следующий пример иллюстрирует результаты, выводимые компонентом Приложение SQL Server Profiler, когда включено событие Deadlock Graph.

Блок-диаграмма потока, показывающая взаимоблокировку пользовательских процессов.

Дополнительные сведения о выполнении события Deadlock Graph в приложении Приложение SQL Server Profiler см. в разделе Анализ взаимоблокировок в приложении SQL Server Profiler.