Обработка транзакций

В этой главе предоставляется обзор внутреннего устройства системы управления транзакциями QHB. В английском языке слово «транзакция» часто сокращается до xact.



Транзакции и идентификаторы

Транзакции могут создаваться явно с помощью команды BEGIN или START TRANSACTION и завершаться с помощью команды COMMIT или ROLLBACK. Операторы SQL вне явных транзакций автоматически используют транзакции из одного оператора.

Каждая транзакция распознается по уникальному идентификатору VirtualTransactionId (также называемому virtualXID или vxid), который состоит из идентификатора обслуживающего процесса (или backendID) и последовательно присваиваемого номера, локального для каждого обслуживающего процесса и называемого localXID. Например, виртуальный идентификатор транзакции 4/12532 состоит из backendID равного 4 и localXID равного 12532.

Невиртуальные TransactionId (или xid), например 278394, последовательно присваиваются транзакциям из глобального счетчика, используемого всеми базами данных в кластере QHB. Это присвоение происходит во время первой записи транзакции в базу данных. Это означает, что транзакции с меньшими xid начинают записывать раньше транзакций с большими xid. Обратите внимание, что порядок, в котором транзакции выполняют свою первую запись в базу данных, может отличаться от порядка, в котором они запускаются, особенно если транзакция начинается с операторов, которые выполняют только чтение базы данных.

Внутренний тип идентификаторов транзакций xid имеет размер 32 бита и его значения повторяются через каждые 4 миллиарда транзакций. После каждого цикла 32-битная эпоха увеличивается. Кроме того, существует 64-битный тип xid8, который включает эту эпоху и поэтому не зацикливается на протяжении жизни установки; посредством приведения его можно преобразовать в xid. Функции в подразделе Функции для получения информации об идентификаторах транзакций и снимках состояния возвращают значения xid8. Идентификаторы используются как основа для механизма параллельного доступа MVCC и потоковой репликации QHB.

Когда транзакция верхнего уровня с (невиртуальным) xid фиксируется, она помечается как зафиксированная в каталоге pg_xact. Дополнительная информация записывается в каталог pg_commit_ts, если включен параметр track_commit_timestamp.

В дополнение к vxid и xid подготовленным транзакциям также присваиваются глобальные идентификаторы транзакций (GID). GID представляют собой строковые литералы длиной до 200 байт, которые должны быть уникальными среди других подготовленных на текущий момент транзакций. Сопоставление GID с xid показано в представлении pg_prepared_xacts.



Транзакции и блокировки

Идентификаторы текущих выполняемых транзакций отображаются в представлении pg_locks в столбцах virtualxid и transactionid. Транзакции только на чтение будут иметь только virtualxid, а в поле transactionid будет NULL, тогда как для транзакций чтения-записи будут установлены оба столбца.

Некоторые типы блокировок ожидают virtualxid, тогда как другие типы ожидают transactionid. Информация о блокировках чтения и записи уровня строк записывается напрямую в заблокированных строках, и ее можно просмотреть с помощью расширения pgrowlocks. Блокировки чтения на уровне строк могут также потребовать присвоения идентификаторов мультитранзакций (mxid; см. подраздел Мультитранзакции и зацикливание).



Субтранзакции

Субтранзакции запускаются внутри транзакций, позволяя разбивать крупные транзакции на более мелкие единицы. Субтранзакции могут фиксироваться или прерываться, не влияя на родительские транзакции, позволяя им продолжаться. Это обеспечивает более легкую обработку ошибок и является распространенным приемом при разработке приложений. В английском языке слово «субтранзакция» часто сокращается до subxact.

Субтранзакции могут запускаться явно с помощью команды SAVEPOINT, но также могут запускаться и другими способами, например, посредством предложения EXCEPTION языка PL/pgSQL. PL/Python и PL/Tcl тоже поддерживают явные субтранзакции. Кроме того, субтранзакции могут запускаться из других субтранзакций. Транзакция верхнего уровня и ее дочерние субтранзакции формируют иерархию или дерево, поэтому мы называем основную транзакцию транзакцией верхнего уровня.

Если субтранзакции присваивается невиртуальный идентификатор, его называют «subxid». Субтранзакциям только на чтение subxid не присваиваются, но как только они попытаются записывать, этот идентификатор будет присвоен. Из-за этого всем родительским транзакциям subxid, вплоть до транзакции верхнего уровня включительно, присваиваются невирутальные идентификаторы. Мы гарантирует, что родительский xid всегда будет меньше любых его дочерних subxid.

Идентификатор непосредственного родителя каждой субтранзакции записывается в каталог pg_subtrans. Для xid транзакций верхнего уровня записей не делается, поскольку у них нет родителя; для субтранзакций только на чтение записи тоже не производятся.

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

Когда фиксируется транзакция верхнего уровня с xid, все ее зафиксированные дочерние субтранзакции тоже записываются как зафиксированные в подкаталоге pg_xact. Если транзакция верхнего уровня прерывается, все ее субтранзакции тоже прерываются, даже если они были зафиксированы.

Чем больше субтранзакций держит открытыми (не откатившимися или освобожденными) каждая транзакция, тем выше издержки, связанные с управлением транзакциями. В разделяемой памяти каждого сервера кешируется до 64 открытых subxid; после этого предела издержки хранения для операций ввода/вывода значительно увеличиваются из-за дополнительных поисков записей о subxid в pg_subtrans.



Двухфазные транзакции

QHB поддерживает протокол двухфазной фиксации (2PC), позволяющий нескольким распределенным системам работать вместе в транзакционной манере. При этом используются команды PREPARE TRANSACTION, COMMIT PREPARED и ROLLBACK PREPARED. Двухфазные транзакции предназначены для использования внешними системами управления транзакциями. QHB придерживается функционала и моделей, предложенных стандартом X/Open XA, но не реализует некоторые не так часто применяемые аспекты.

Когда пользователь выполняет PREPARE TRANSACTION, единственно возможными следующими командами являются COMMIT PREPARED или ROLLBACK PREPARED. В целом, предполагается, что это подготовленное состояние длится очень недолго, но проблемы с внешней доступностью могут означать, что транзакции останутся в этом состоянии продолжительное время. Короткоживущие подготовленные транзакции хранятся только в разделяемой памяти и WAL. Транзакции, которые проходят контрольные точки, записываются в каталог pg_twophase. Текущие подготовленные транзакции можно просмотреть в представлении pg_prepared_xacts.