Deadlocks

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

Deadlocks

Информация

Компьютеры, программирование

Другие материалы по предмету

Компьютеры, программирование

Сдать работу со 100% гаранией
6 2034106287 Tbl 0 TAB IS GRANT

 

61 6 2034106287 Tbl 0 PAG 1:17495 IX GRANT

61 6 2034106287 Tbl 0 RID 1:17495:1 X CNVT

61 6 2034106287 Tbl 0 TAB IX GRANTТе, что (в моем случае) от spid 54 это наложенные ранее, от первой транзакции, а те, у которых spid 61 - от второй. С блокировками намерения все то же самое, они запрошены и успешно получены. А вот с эксклюзивными ситуация такая: сначала, выполняя SELECT, мы получили разделяемую блокировку на ту же запись (RID 1:17495:1), что и первая транзакция. Затем нам понадобилось туда же записать, а для этого надо сконвертировать коллективную блокировку S до X. Однако сделать это не получается, так как мешает S-блокировка на ту же запись от первой транзакции. Что мы и видим в третьей снизу строчке, статус эксклюзивной блокировки (X) CNVT конвертирование. То есть SELECT выполнился, но до UPDATE дело не дошло, T2 ждет, пока T1 освободит запись X=2, чтобы наложить эксклюзивную блокировку.

Переключимся обратно в первое окно и попытаемся завершить T1:

UPDATE Tbl SET Y=3 WHERE X=2

COMMIT TRANТеперь и T1 будет ждать, пока T2 освободит свою коллективную блокировку. Таким образом, транзакции будут ожидать друг друга, цикл в графе ожидания замкнется и, некоторое время спустя, когда менеджер блокировок это обнаружит, одна из транзакций будет отменена. Приложение, запустившее ее, получит сообщение 1205 о взаимоблокировке (Transaction (Process ID 61) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction), а другая транзакция завершится успешно.

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

Способы устранения

Поскольку взаимоблокировка произошла из-за того, что транзакции удерживали коллективные блокировки и потом попытались их повысить до эксклюзивных, то, в принципе, помочь избежать неприятностей в данном случае сможет понижение уровня изоляции до READ COMMITED. При этом коллективная блокировка не будет держаться до конца транзакции, а снимется сразу после завершения чтения, а значит, обновить записи ничто не помешает. Но тогда вместо взаимоблокировки мы вполне можем получить неверные данные, так как между SELECT и UPDATE сможет втиснуться другая транзакция, которая изменит Y и данные, полученные SELECT на момент UPDATE, окажутся неактуальными, чего в некоторых случаях допускать нельзя.

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

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

SELECT @Var = Y FROM Tbl WITH (UPDLOCK) WHERE X = 2Третий пример

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

Подготовим две транзакции в разных окнах QA и, соответственно, в разных подключениях.

Первая транзакция: T1

-- установим необходимый уровень изоляции

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

BEGIN TRAN

UPDATE Tbl SET X = 4 WHERE X = 4 - обновим строку X=4

WAITFOR DELAY '00:00:10'

UPDATE Tbl SET X = 6 WHERE X = 6 - обновим строку X=6

COMMIT TRANСтоит заметить, что уровень изоляции в данном случае соответствует уровню изоляции, выставляемому по умолчанию, и делать его ниже крайне не рекомендуется. Оператор WAITFOR нужен для того, чтобы сервер исполнял команды не сразу друг за другом, а с разрывом в 10 секунд, чтобы мы успели переключиться во второе окно и стартовать вторую транзакцию, имитируя тем самым одновременность их выполнения.

Вторая транзакция: T2

--- установим необходимый уровень изоляции

SET TRANSACTION ISOLATION LEVEL READ COMMITTED BEGIN TRAN

UPDATE Tbl SET X = 2 WHERE X = 2 - обновим строку X=2

COMMIT TRANЕстественно, совершенно не важно, какие значения мы запишем в X в этих транзакциях, в данном случае важно лишь условие выборки.

Запустив T1, а затем, переключившись и запустив T2, мы получим взаимоблокировку. Обратите внимание, что на первый взгляд транзакции вполне безобидны. Более того, условия никак не пересекаются по диапазонам, в первом случае затрагиваются строки X = 4 и X = 6, а во втором X = 2. Можно пойти еще дальше, и изменить в T2 условие таким образом:

UPDATE Tbl SET Y = 10 WHERE Y = 10Тогда условия выборки не будет пересекаться даже по полям! Но взаимоблокировка все равно произойдет.

Как уже упоминалось выше, в реальной ситуации найти виновные транзакции бывает достаточно проблематично, так как по умолчанию известен только процесс, запустивший транзакцию-«жертву» и, как правило, этого недостаточно.

Определение «виновных» транзакций

Существует возможность заставить сервер выдать более полную информацию об ошибке. Однако не следует этой возможностью злоупотреблять, так как производительность сервера при этом серьезно понижается. Для более тонкой настройки сервер поддерживает флаги трассировки (trace flags). Некоторые из этих флагов предназначены для получения более полной информации об ошибках. Флаги устанавливаются с помощью команды DBCC TRACEON (flag,…), а снимаются, соответственно с помощью DBCC TRACEOFF (flag,…).

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

1204 сбор расширенной информации о взаимоблокировке.

3605 выдача информации в EventLog.

3406 выдача информации в файл errorlog.

-1 сбор информации изо всех сессий.

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

1200 сбор информации о порядке наложения блокировок (недокументированный).

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

Запустить SQL Profiler, специальную программу для отслеживания работы сервера, и настроить в ней перехват ошибок (event class Errors and Warnings: Exception and Error Log), а затем выставить флаг трассировки 3605. В этом случае вся дополнительная информация о работе SQL-сервера будет сбрасываться в Event Log и перехватываться профайлером, где ее в последствии можно будет посмотреть.

Выставить флаг отладки 3406. В этом случае вся дополнительная информация будет сбрасываться в файл errorlog, который по умолчанию находится в каталоге LOG директории SQL сервера.

СОВЕТ

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

DBCC TRACEON(1204, 3406, -1)Затем выполним T1 в одном окне, потом переключимся в другое, и выполним T2. После того как случится взаимоблокировка, дополнительную информацию о ней можно будет найти в файле errorlog, и среди прочих данных можно будет наблюдать примерно следующее:

Deadlock encountered .... Printing deadlock information

23:51:28.00 spid4

23:51:28.00 spid4 Wait-for graph

23:51:28.00 spid4

23:51:28.00 spid4 Node:1

23:51:28.00 spid4 RID: 7:1:50:1 CleanCnt:1 Mode: X Flags: 0x2

23:51:28.00 spid4 Grant List 0::

23:51:28.00 spid4 Owner:0x19333de0 Mode: X Flg:0x0 Ref:0 Life:02000000 SPID:53 ECID:0

23:51:28.00 spid4 SPID: 53 ECID: 0 Statement Type: UPDATE Line #: 1

23:51:28.01 spid4 Input Buf: Language Event:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

BEGIN TRAN

UPDATE Tbl SET X=2 WHERE X=2

COMMIT TRAN

23:51:28.01 spid4 Requested By:

23:51:28.01 spid4 ResType:LockOwner Stype:'OR' Mode: U SPID:51

ECID:0 Ec:(0x19401548) Value:0x19333da0 Cost:(0/54)

23:51:28.01 spid4

23:51:28.01 spid4 Node:2

23:51:28.01 spid4 RID: 7:1:50:3 CleanCnt:1 Mode: X Flags: 0x2

23:51:28.01 spid4 Grant List 0::

23:51:28.01 spid4 Owner:0x19333d20 Mode: X Flg:0x0 Ref:0 Life:02000000 SPID:51 ECID:0

23:51:28.01 spid4 SPID: 51 ECID: 0 Statement Type: UPDATE Line #: 1

23:51:28.01 spid4 Input Buf: Language Event:

Похожие работы

<< < 1 2 3 4 5 6 7 > >>