Отображение информации блокировки Информация о блокировке может быть отображена динамически управляемым представлением, называемым sys.dm_tran_locks.
Представление sys.dm_tran_locks возвращает информацию о текущей активной блокировке менеджера ресурсов. Каждая строка отображает активный в настоящий момент запрос на блокировку, которая была предоставлена или предоставление которой ожидается.
Столбцы этого представления соответствуют двум группам: ресурсам и запросам. Группа ресурсов, описывает ресурсы, которым предоставлена блокировка на основании запросов, а группа запросов описывает запросы на блокировку. Наиболее важными столбцами этого представления являются следующие:
· resource_type указывает тип ресурса;
· resource_database_id задает идентификатор базы данных, в которой находится этот ресурс;
· request_mode задает режим запроса;
· request_status задает текущее состояние запроса.
Пример:запрос дляотображения всех блокировок, которые находятся в состоянии ожидания.
SELECT resource_type, DB_NAME(resource_database_id) as db_name,
request_session_id, request_mode, request_status FROM sys.dm_tran_locks
WHERE request_status = 'WAIT;'
Пример:
Для данного запроса обновления строки используется Соединение 1 и транзакция, для которой установлена монопольная блокировка на обновление данных, не завершена (остается открытой).
USE TSQLFundamentals2008;
-- Соединение 1
BEGIN TRAN;
UPDATE Production.Products
SET unitprice = unitprice + 1.00
WHERE productid = 2;
Для Соединения 2 запрашиваем ту же самую строку (этот сеанс блокируется)
-- Соединение 2
SELECT productid, unitprice
FROM Production.Products
WHERE productid = 2;
Для Соединения 3 обращаемся к представлению sys.dm_tran_locks
SELECT
request_session_id AS spid,
resource_type AS restype,
resource_database_id AS dbid,
DB_NAME(resource_database_id) AS dbname,
resource_description AS res,
resource_associated_entity_id AS resid,
request_mode AS mode,
request_status AS status
FROM sys.dm_tran_locks;
Полученный результат:
Взаимная блокировка
Взаимная блокировка является особой проблемой одновременной paботы с базой данных, при которой две транзакции блокируют выполнение друг друга. Первая транзакция имеет блокировку на некоторый объект базы данных, к которому другая транзакция ожидает доступа, и наоборот. (Обычно несколько транзакций могут вызвать взаимную блокировку, когда они создают циклические зависимости.)
Взаимоблокировка возникает, когда две и более задач постоянно блокируют друг друга в ситуации, когда у каждой задачи заблокирован ресурс, который пытаются заблокировать другие задачи. На следующем графике приведена общая схема состояния взаимоблокировки, в которой:
· Задача T1 блокирует ресурс R1 (изображается в виде стрелки, направленной от R1 к T1) и запросила блокировку ресурса R2 (изображается в виде стрелки, направленной от T1 к R2).
· Задача T2 блокирует ресурс R2 (изображается в виде стрелки, направленной от R2 к T2) и запросила блокировку ресурса R1 (изображается в виде стрелки, направленной от T2 к R1).
· Так как ни одна из задач не может продолжиться до тех пор, пока не будет доступен ресурс, а ни один из ресурсов не может быть освобожден до тех пор, пока задание не продолжится, наступает состояние взаимоблокировки.
Рис.
"Мертвые", или тупиковые, блокировки характерны для многопользовательских систем. "Мертвая" блокировка возникает, когда две транзакции блокируют два блока данных и для завершения любой из них нужен доступ к данным, заблокированным ранее другой транзакцией. Для завершения каждой транзакции необходимо дождаться, пока блокированная другой транзакцией часть данных будет разблокирована. Но это невозможно, так как вторая транзакция ожидает разблокирования ресурсов, используемых первой.
Без применения специальных механизмов обнаружения и снятия "мертвых" блокировок нормальная работа транзакций будет нарушена. Если в системе установлен бесконечный период ожидания завершения транзакции (а это задано по умолчанию), то при возникновении "мертвой" блокировки для двух транзакций вполне возможно, что, ожидая освобождения заблокированных ресурсов, в тупике окажутся и новые транзакции.
Чтобы избежать подобных проблем, в среде MS SQL Server реализован специальный механизм разрешения конфликтов тупикового блокирования. Компонент SQL Server Database Engine автоматически обнаруживает цикл взаимоблокировки в SQL Server. Компонент Database Engine для устранения взаимоблокировки выбирает один из сеансов в качестве жертвы взаимоблокировки и прекращает выполнение текущей транзакции с ошибкой.
Для этих целей сервер снимает одну из блокировок, вызвавших конфликт, и откатывает инициализировавшую ее транзакцию. При выборе блокировки, которой необходимо пожертвовать, сервер исходит из соображений минимальной стоимости.
Полностью избежать возникновения "мертвых" блокировок нельзя. Хотя сервер и имеет эффективные механизмы снятия таких блокировок, все же при написании приложений следует учитывать вероятность их возникновения и предпринимать все возможные действия для предупреждения этого. "Мертвые" блокировки могут существенно снизить производительность, поскольку системе требуется достаточно много времени для их обнаружения, отката транзакции и повторного ее выполнения.
Для минимизации возможности образования "мертвых" блокировок при разработке кода транзакции следует придерживаться следующих правил:
· выполнять действия по обработке данных в постоянном порядке, чтобы не создавать условия для захвата одних и тех же данных;
· избегать взаимодействия с пользователем в теле транзакции;
· минимизировать длительность транзакции и выполнять ее по возможности в одном пакете;
· применять как можно более низкий уровень изоляции (см. далее).
Пример: ситуация взаимной блокировки между двумя транзакциями. Примечание: параллельность работы не может быть достигнута естественным образом при использовании малой по размерам базы данных sample, потому что каждая транзакция в ней выполняется очень быстро. В примере используется оператор waitfor DELAY для остановки выполнения транзакций на десять секунд для симуляции взаимной блокировки.
Первое соединение
BEGIN TRANSACTION
UPDATE employee
SET dept_no = 'd2'
WHERE emp_no = 9031
WAITFOR DELAY '00:00:10'
DELETE FROM works_on
WHERE emp_no = 18316
AND project_no = 'p2'
Система возвращает сообщение:
(1 row(s) affected)
Msg 1205, Level 13, State 45, Line 6
Transaction (Process ID 57) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Второе соединение
BEGIN TRANSACTION
UPDATE works_on
SET job = 'Manager'
WHERE emp_no = 18316
AND project_no = 'p2'
WAITFOR DELAY '00:00:10'
UPDATE employee
SET emp_lname = 'Green'
WHERE emp_no = 9031
COMMIT
Можно повлиять на то, какая транзакция будет выбрана в качестве «жертвы», используя опцию deadlock_priority в операторе set. Существует 21 уровень приоритетов: значения от -10 до 10. Значение low соответствует -5, знание normal (значение по умолчанию) соответствует 0, a high соответствует 5. "Жертва'' сессии выбирается в соответствии с установленными в сессии приоритетами взаимных блокировок.
Уровни изоляции
В теории каждая транзакция должна быть полностью изолирована от всех других транзакций. Однако в подобном случае объем доступных данных значительно сокращается, потому что операции чтения в транзакции блокируются операциями записи в других транзакциях, и наоборот. Если доступность данных является важным требованием, это свойство должно быть ослаблено с использованием уровней изоляции.
Уровни изоляции задают степень, в которой обрабатываемые в транзакции данные будут защищены от изменений в других транзакциях.
Уровни изоляцииопределяют поведение параллельно работающих пользователей, читающих и записывающих данные.
|