Глобальный дедлок и его обнаружение в локальной базе данных PostgreSQL

Kate

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

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

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

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

Для начала в этом посте блога описывается, что такое глобальный дедлок и как он обрабатывается в существующем сейчас PostgreSQL. После этого будет рассмотрена обработка дедлока в распределенной среде.

Что такое дедлок?

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

Рис.1 Блокировка обеспечивает последовательную работу каждой транзакции с объектами базы данных


Рис.1 Блокировка обеспечивает последовательную работу каждой транзакции с объектами базы данных

Обычно блокировка имеет более одного режима, и некоторые из них позволяют нескольким транзакциям использовать ее одновременно. Другие режимы позволяют получить блокировку только одной транзакции. В типовых базах данных, включая PostgreSQL, такие блокировки снимаются только тогда, когда удерживающая транзакция завершается фиксацией или прерыванием. Это называется двухфазной блокировкой (2PL, обратите внимание, что она отличается от двухфазной фиксации, 2PC) и необходима для обеспечения сериализации транзакций.

В некоторых ситуациях существует вероятность того, что две транзакции будут ждать друг друга.

Рис.2 Как может возникнуть дедлок


Рис.2 Как может возникнуть дедлок

Как показано на рисунке 2, транзакция (T1) получает эксклюзивную блокировку на таблицу (TabA), а другая транзакция (T2) - на таблицу (TabB). Далее, T1 пытается получить эксклюзивную блокировку на TabB. Затем T1 начинает ждать завершения T2. После этого предположим, что T2 пытается получить эксклюзивную блокировку на TabA.

Рис.3 Дедлок-ситуация


Рис.3 Дедлок-ситуация

В этой ситуации T1 ждет T2, а T2 ждет T1 (Рисунок 3). Нет никакого способа продолжить работу, пока вы не прервете T1 или T2 извне.

Такая ситуация называется дедлоком. На рисунке 3 представлен простейший сценарий дедлока, в реальности ситуация может быть гораздо сложнее. В ядре базы данных стратегия внутренних блокировок тщательно разработана для предотвращения подобных сценариев. Однако дедлок может возникнуть только при нагрузке на приложение, и предотвратить возникновение такого сценария в целом не представляется возможным.

Почему необходимо обнаружение ситуаций с дедлоками

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

Шаги по обнаружению дедлока

Во многих учебниках по базам данных описывается, как обнаружить дедлок. Для его обнаружения мы используем подход "граф ожидания" (wait-for-graph). Это простой и проверенный способ.

Рис.4 Граф ожидания


Рис.4 Граф ожидания

Чтобы обнаружить дедлок, необходимо проанализировать, что ожидает та или иная транзакция. Рисунок 4 - это очень простой пример графа ожидания.

Рис.5 При наступлении дедлока граф ожидания содержит цикл


Рис.5 При наступлении дедлока граф ожидания содержит цикл

Также известно, что граф ожидания содержит "цикл", когда возникает дедлок в виде [1][2][3][4], как показано на рисунке 5. Вы можете использовать этот цикл в графе ожидания для обнаружения дедлока.

В PostgreSQL, когда транзакция не может получить запрошенную блокировку в течение определенного времени (задается параметром `deadlock_timeout`, значение по умолчанию 1 секунда), то запускается обнаружение дедлока.

Начиная с транзакции, которая не смогла получить блокировку, PG проверяет (более чем одну) другие транзакции, которые могут удерживать ожидаемую блокировку. Затем PG проверяет, ожидают ли транзакции другой блокировки. Повторяя это, можно построить граф ожидания. PG перебирает все возможные сегменты графа ожидания, пока он не завершится транзакцией, не ожидающей блокировки, или не найдет "цикл", когда граф достигает той же транзакции в самом начале.

Рис.6 Архитектура обнаружения дедлока в PostgreSQL


Рис.6 Архитектура обнаружения дедлока в PostgreSQL

На рисунке 6 показано простое и схематичное описание этого.

Сценарий глобального дедлока

Подобные ситуации могут возникать в средах с несколькими базами данных (или распределенными базами данных), когда транзакция охватывает более одной базы. Это называется "глобальным дедлоком".

Рис.7 Сценарий дедлока с участием более чем одной базы данных


Рис.7 Сценарий дедлока с участием более чем одной базы данных

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

В подобной ситуации при использовании настоящей PG транзакция, ожидающая удаленные транзакции, не ждет никакой блокировки, и у вас нет средств для отслеживания такого внутрикластерного графа ожидания.

Также известно, что даже в такой распределенной транзакции вы можете использовать внутрикластерный граф ожидания для обнаружения глобального дедлока [2][4].

 
Сверху