Транзакции в PostgreSQL

Реляционная теория и SQL позволяет абстрагироваться от конкретной реализации СУБД, но есть одна непростая проблема: как обеспечить параллельную работу множества сессий (concurrency), которые модифицируют данные, так, чтобы они не мешали друг другу ни с точки зрения чтения, ни с точки зрения записи и обеспечивали целостность данных (consistency) и их надежность (durability)?

Ответ – транзакционные системы OLTPOnLine Transaction Processing. Они отвечают следующему принципу ACID:

  • Atomicity – атомарность
  • Consistency – согласованность
  • Isolation – изолированность
  • Durability – долговечность

Соответственно, транзакция (transaction) это:

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

Всё было бы хорошо, но что делать с блокировками и сбоями?

Для этого придуманы следующие принципы:

ARIESAlgorithms for Recovery and Isolation Exploiting Semantics. Алгоритмы эффективного восстановления после сбоев.

Использует следующие механизмы:

  • logging – логирование
  • undo – сегменты отката транзакций
  • redo – сегменты проигрывания транзакций после сбоя
  • checkpoints – система контрольных точек

MVCC MultiVersion Concurrency Control. Конкурентный контроль мультиверсионности. Использует следующие механизмы:

  • copy-on-write – создаётся копия старых данных при записи или модификации
  • каждый пользователь работает со снимком БД
  • вносимые пользователем изменения не видны другим до фиксации транзакции
  • практически не использует блокировок (только одна – писатель блокирует писателя, если они пытаются работать с одной записью)
  • или ручная блокировка при вызове команды select for update

Механизм реализации MVCC в PostgreSQL.

Данные хранятся поблочно и посмотрим, из чего состоит один блок:

В самом начале идёт служебный блок, состоящий из:

  • пары идентификаторов транзакций по 4 байта
  • ряда информационных байтов
  • пары информационных масок
  • пустой битовой карты для будущего использования в следующих версиях PostgreSQL
  • данных (более подробно будем рассматривать в следующих темах).

Если рассматривать подробно, то заголовок состоит из:

  • xmin – идентификатор транзакции, которая создала данную версию записи
  • xmax – идентификатор транзакции, которая удалила данную версию записи
  • cmin – порядковый номер команды в транзакции, добавившей запись
  • cmax – номер команды в транзакции, удалившей запись

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

  • Insert – добавляется новая запись с xmin = txid_current() и xmax = 0
  • Update – в старой версии записи xmax = txid_current(), то есть делается delete, добавляется новая запись с xmin = txid_current() и xmax = 0, то есть делается insert
  • Delete – в старой версии записи xmax = txid_current()

Следовательно, при добавлении записи у нас происходит две операции: старая запись помечается как удалённая (проставляется xmax), и происходит добавление новой записи. При удалении проставляется значение xmax, при этом физическое удаление данных не производится.

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

  • xmin_commited, xmin_aborted – xmin подтверждён или отменён
  • xmax_commited, xmax_aborted – xmax подтверждён или отменён

Важно отметить поле ctid – ссылка на следующую, более новую, версию той же строки. У самой новой, актуальной, версии строки ctid ссылается на саму эту версию. Номера ctid имеют вид (x,y): здесь x – номер страницы, y – порядковый номер указателя в массиве

Каждая транзакция может завершиться:

  •  успешно – после команды commit, для этого, в зависимости от типа операции, проставляются биты xmin_commited, xmax_commited
  • неуспешно из-за, например, ошибки доступа к данным или вызове команды rollback.

При неуспешном завершении нам нужно откатить все изменения, выполненные этой транзакцией. При этом в случае с PostgreSQL нам практически не нужно ничего делать – будут установлены биты подсказки xmin_aborted, xmax_aborted. Сам номер xmax при этом остаётся в странице, но смотреть на него уже никто не будет.

Так что операции как коммита, так и роллбэка транзакции в PostgreSQL одинаково быстры.

В следующей статье мы посмотрим на практике, как работают транзакции.

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

двадцать + 4 =