Рис. 40.1. Состояния внутренних блокировок Firebird
* 4 - совместно используемая запись (shared write), другой обычный режим блокировки таблицы. Совместно используемая запись совместима с разделяемым чтением и другими совместно используемыми записями, но не с любым защищенным режимом.
* 5 - защищенная запись (protected write), которая допускает разделяемое чтение и пустую блокировку и ничего больше. Защищенная запись используется при режиме CONSISTENCY и для такой блокировки базы данных, что обычные пользователи не могут получить к ней доступ.
* 6 - исключительный доступ (exclusive), используется для внутренних структур, когда параллельный доступ может повлиять на изменения или привести к тому, что вторая транзакция будет читать незавершенные изменения данных.
Таблица блокировок
Менеджер блокировок управляет таблицей блокировок для координации совместного использования ресурсов в клиентских потоках. Информация, предоставляемая здесь, может быть полезной при попытках исправить ситуации взаимных блокировок, например;
* все в настоящий момент заблокировано в системе в своем состоянии;
* глобальная статистика заголовков, такая как размер таблицы блокировок, множество свободных блокировок, множество взаимных блокировок и т.д.;
* флаги процессов, указывающие, была ли передана блокировка или она осуществляет ожидание.
Блокировки сохраняются в последовательностях, каждая последовательность идентифицируется номером в соответствии с типом блокируемого ресурса. Номера последовательностей объясняются в табл. 40.5.
Запущенная транзакция использует таблицу блокировок в качестве доски объявлений. Чтобы исключить сборку мусора версий записей, которые нужны другой транзакции, каждая транзакция должна знать самое старое действие, которое видит другая транзакция. Вот как это сделано:
1. При старте транзакция сохраняет в области данных собственного блока идентификатор старейшей транзакции, которая еще выполняется (активна).
2. Затем она устанавливает пустую блокировку на все параллельные транзакции. Когда будет получен каждый блок, ей возвращается содержимое области данных.
Новая транзакция проверяет блок каждой существующей транзакции для отыскания идентификатора старейшей транзакции, о которой знает каждая активная транзакция.
Списки владельцев, ресурсов и запросов представлены в виде цепочки с возможностью проходить по ним вперед и назад. В начале каждого блока находится указатель на следующий блок. Списки указателей используются Менеджером блокировок, когда ему нужно разместить новый блок и постараться найти свободные блоки для повторного использования. Он выделит новый блок, только если не существует свободных блоков нужного типа и размера.
Свободные элементы содержат прямые и обратные указатели на первый и последний свободный блок соответственно.
Взаимная блокировка (deadlock) возникает, когда владелец А хочет блокировать ресурс 1, который заблокирован владельцем В, а владелец В хочет заблокировать ресурс 2, который заблокирован процессом А. Она также может появиться для одного ресурса, если два владельца начинают с блокировки чтения и запрашивают блокировки на запись.
Эту ситуацию владельцы не могут разрешить без постороннего вмешательства. Решение осуществляется, когда взаимная блокировка определяется последующим сканированием и Менеджер блокировок возвращает ошибку одному владельцу или другому. Интервал сканирования блокировок по умолчанию - параметр DeadiockTimeout в файле конфигурации- составляет 10 секунд. Этот интервал не используется при условиях, где присутствует WAIT. Ожидание является нормальным в системе, которая управляет параллельными изменениями, и не требует затрат на сканирование.
Сообщения о взаимных блокировках могут не помочь точно указать на "проблемы deadlock", наблюдаемые в ваших приложениях. Взаимные блокировки всегда включают двух владельцев (или две раздельные транзакции), каждый из которых мешает другому. Firebird имеет привычку выдавать клиентам сообщения "взаимные блокировки" при большинстве конфликтов блокировок, хотя истинная взаимная блокировка - как было описано ранее - довольно редкое явление.
* Ошибки, возвращаемые как "конфликт блокировки" при запросах блокировки NO WAIT, не фиксируются в таблице блокировок как взаимные блокировки, потому что ожидает только один владелец.
* Ошибки, возвращаемые как "взаимные блокировки" с последующим сообщением "Update conflicts with concurrent update" (Конфликты обновлений при параллельном обновлении), также не являются настоящими взаимными блокировками. В этом случае произошло всего лишь то, что один владелец изменил (или удалил) строку и "пошел" дальше. Другой параллельный владелец попытался изменить (или удалить) ту же самую запись, ждал освобождения ее первым владельцем, а теперь получил ошибку, потому что последняя подтвержденная версия записи была изменена.
Таблица блокировок данных может быть выведена в более или менее читаемом формате с помощью утилиты Lock Print.
Утилита Lock Print
Программой, которая выбирает статистику таблицы блокировки, является исполняемый модуль fb_lock_print, который можно найти в каталоге /bin каталога инсталляции Firebird. (Для версии 1.0.x ищите iblockpr.exe в Windows или gds_lock_pr в POSIX.) Доступны два синтаксиса: один для статичного отчета, другой для задания интерактивной выборки в указанных интервалах.
Синтаксис для Firebird 1.5 и выше:
fb_lock jprint <переключатели>
Версия 1.0.x, POSIX:
gds_lock_pr <переключатели>
Версия 1.0.x, Windows:
iblockpr <переключател14>
Программа fb lock print имеет множество переключателей, описанных в табл. 40.1. Когда не задано ни одного переключателя, fb lock print выводит итоговую информацию, описывающую заголовок блока и владельцев, связанных с Менеджером блокировок.
Таблица 40.1. Переключатели для отчетов Lock Print
Переключатель |
Описание |
(нет переключателей) |
Выводит итоговую информацию, описывающую заголовок блока и владельцев, связанных с Менеджером блокировок |
-a |
Выводит содержимое таблицы блокировок, включая заголовок блока, группы блоков, группы владельцев и группы запросов. Группа блока представляет ресурс, который должен быть заблокирован (база данных, транзакция, отношение, страница базы данных и т.д.), и идентифицирует владельца, который имеет или запросил блокирование этого объекта. Группа запроса описывает запрос в процессе блокирования ресурса. Группа запроса может описывать или предоставленную блокировку, или ожидание завершения запроса на блокировку |
-с |
Указывает, что таблица блокировок должна быть скопирована, а не использована сама. Копирование является быстрым и создает мгновенный статичный снимок таблицы блокировок. Тем не менее он остановит все процессы доступа к базе данных на время своего выполнения |
-f |
Задает, что анализ должен быть выполнен на указанном файле, а не на настоящем файле блокировки. К сожалению, этот переключатель не работает в Firebird |
-h |
Выводит только историю |
-i <переключатели> <t> <n> |
Запускает интерактивный режим (см. разд. "Интерактивные отчеты"). Если указано только -i, то выводятся все данные |
-l |
Выводит только группы блоков |
-n |
Указывает, что не существует "никакого моста". Мост является переходным механизмом, распознающим множество серверов различных версий Firebird на одной машине. Это не доступно в версиях Firebird 1.0.x или 1.5, но, скорее всего, будет реализовано в следующих версиях Firebird |