Обнаружение и устранение взаимоблокировок
Взаимоблокировка возникает, когда две и более задач постоянно блокируют друг из-за того, что задача каждой из сторон блокирует ресурс, необходимый другой стороне. На следующем графике приведена общая схема состояния взаимоблокировки, в которой:
Задача 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, а если он включен, значение для активных пакетов равно от 1 до n. При отсутствии активных пакетов в сеансе BatchID присваивается значение 0. Mode. Задает тип блокировки для конкретного ресурса, который запрошен, предоставлен или ожидается потоком. Значением Mode может быть IS (с намерением совмещаемого доступа), S (совмещаемая), U (на обновление), IX (с намерением монопольного доступа), SIX (совмещаемая с намерением монопольного доступа) и X (монопольная). Дополнительные сведения см. в разделе Режимы блокировки. Line # (line для флага трассировки 1222). Содержит номер строки в текущем пакете инструкций, который выполнялся в момент возникновения взаимоблокировки. Input Buf (inputbuf для флага трассировки 1222). Выводит все инструкции в текущем пакете. |
Node. Представляет номер записи в цепочке взаимоблокировки. Lists. Владелец блокировки может быть частью этих списков:
Statement Type. Описывает тип инструкции DML (SELECT, INSERT, UPDATE или DELETE), для которой потокам выданы разрешения. Victim Resource Owner. Задает участвующий поток, который SQL Server выбирает в качестве жертвы, чтобы нарушить цикл взаимоблокировки. Выбранный поток и все его субпотоки прекращаются. Next Branch. Представляет два или более субпотока из одного SPID, которые участвуют в цикле взаимоблокировки. |
deadlock victim. Представляет собой адрес физической памяти задачи (см. раздел sys.dm_os_tasks), которая была выбрана в качестве жертвы взаимоблокировки. Может быть равен 0 (нулю) в случае неустраненной взаимоблокировки. Откатываемая задача не может быть выбрана в качестве жертвы взаимоблокировки. executionstack. Представляет код Transact-SQL, выполняющийся в момент возникновения взаимоблокировки. priority Представляет собой приоритет в случае взаимоблокировки. В определенных случаях компонент Database Engine может отдать предпочтение изменению приоритета в случае взаимоблокировки на некоторое короткое время для достижения лучшего параллелизма. logused Пространство журнала, используемое задачей. owner id. Идентификатор транзакции, которая управляет запросом. status. Состояние задачи. Принимает одно из следующих значений:
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 представляется одним из следующих способов:
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=AdventureWorks.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=AdventureWorks.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=AdventureWorks.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=AdventureWorks.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.