Рубрики
Без рубрики

Как работают консультативные блокировки PostgreSQL

Автор оригинала: Vlad Mihalcea.

PostgreSQL , как и многие современные СУБД, предлагает как MVCC (Управление параллелизмом нескольких версий), так и и явная пессимистическая блокировка для различных случаев использования, когда вам нужен пользовательский механизм управления параллелизмом.

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

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

  • эксклюзивные консультативные замки
  • общие консультативные блокировки

Эксклюзивные консультативные замки

Эксклюзивная консультативная блокировка блокирует любую эксклюзивную или общую консультативную блокировку на одном и том же ключе блокировки.

Общие консультативные блокировки

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

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

  • Консультативные замки сессионного уровня
  • Консультативные блокировки на уровне транзакций

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

Консультативные замки сессионного уровня

PostgreSQL определяет несколько функций API, которые позволяют получить консультативную блокировку на уровне сеанса:

void pg_advisory_lock(bigint key)
void pg_advisory_lock(int… key)

boolean pg_try_advisory_lock(bigint key)
boolean pg_try_advisory_lock(int… key)

void pg_advisory_lock_shared(bigint key)
void pg_advisory_lock_shared(int… key)

boolean pg_try_advisory_lock_shared(bigint key)
boolean pg_try_advisory_lock_shared(int… key)

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

Консультативные блокировки на уровне сеанса должны быть освобождены явно. Даже если консультативные блокировки являются реентерабельными, если вы приобрели один и тот же замок дважды, вам также необходимо освободить его дважды.

Чтобы освободить консультативную блокировку на уровне сеанса, вы можете использовать одну из следующих функций PostgreSQL:

void pg_advisory_unlock(bigint key)
void pg_advisory_unlock(int… key)

void pg_advisory_unlock_shared(bigint key)
void pg_advisory_unlock_shared(int… key)

void pg_advisory_unlock_all()

Консультативные блокировки на уровне транзакций

Чтобы получить консультативную блокировку на уровне транзакций, вам необходимо использовать одну из следующих функций PostgreSQL:

void pg_advisory_xact_lock(bigint key)
void pg_advisory_xact_lock(int… key)

boolean pg_try_advisory_xact_lock(bigint key)
boolean pg_try_advisory_xact_lock(int… key)

void pg_advisory_xact_lock_shared(bigint key)
void pg_advisory_xact_lock_shared(int… key)

boolean pg_try_advisory_xact_lock_shared(bigint key)
boolean pg_try_advisory_xact_lock_shared(int… key)

Вам не нужно освобождать эти блокировки, так как они автоматически освобождаются в конце родительской транзакции.

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

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

Как правило, эта задача очень хорошо подходит для механизма блокировки чтения/записи. Однако мы не можем использовать Java ReadWriteLock , так как это будет работать только для одной JVM (одного узла).

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

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

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

Почему бы вместо этого не использовать блокировки на уровне строк?

Хотя PostgreSQL предлагает возможность приобретать эксклюзивные и общие блокировки для каждой строки таблицы, это не всегда удобно. Что делать, если мы храним метаданные документа в Апач Кассандра ? Чтобы использовать блокировки на уровне строк, нам нужно сохранить строку для каждого документа, которым мы управляем. Но если у нас миллиарды документов, мы не хотим, чтобы в итоге получилась таблица с миллиардом строк, единственная цель которой-позволить нам получить общую или эксклюзивную блокировку для каждой строки.

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