linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it worksскоро
  • Уроки
  • База знаний
  • Собеседование
home/postgres/kb/MVCC и видимость/subtransactions

kb/mvcc ── MVCC и видимость ── intermediate

Вложенные транзакции и savepoints

SAVEPOINT открывает вложенную транзакцию (подтранзакцию) внутри основной. ROLLBACK TO SAVEPOINT откатывает её работу, не трогая остальную транзакцию. Каждая пишущая подтранзакция получает свой xid, а связь «подтранзакция - родитель» хранит pg_subtrans. Блоки EXCEPTION в PL/pgSQL - это тоже подтранзакции.

view as markdownaka: savepoints, pg-subtrans

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

Откат части работы

sql
BEGIN;
INSERT INTO mv VALUES (2, 'sub');
SAVEPOINT s1;
INSERT INTO mv VALUES (3, 'oops');
ROLLBACK TO SAVEPOINT s1;   -- отменили только вставку (3,'oops')
COMMIT;                     -- (2,'sub') осталась, (3,'oops') - нет

ROLLBACK TO SAVEPOINT отматывает всё, что случилось после точки, но сама транзакция продолжает жить. Это не то же самое, что полный ROLLBACK: работа до точки сохранения цела.

Что происходит внутри

Каждая точка сохранения открывает подтранзакцию. Как только подтранзакция что-то пишет, ей выдаётся собственный xid (subxid). Версии строк, созданные внутри неё, помечаются этим subxid в xmin. Связь «этот subxid принадлежит такой-то родительской транзакции» хранит каталог pg_subtrans.

Когда подтранзакция откатывается, её subxid помечается как aborted в clog (см. clog-hint-bits). После этого все версии с таким xmin становятся невидимыми - правило видимости xmin-xmax отсекает их так же, как версии откатившейся обычной транзакции. Физически строки остаются на странице мусором, пока их не уберёт vacuum.

Что важно: внешний pg_current_xact_id() показывает верхний xid транзакции, а не subxid - подтранзакции остаются внутренней механикой.

EXCEPTION - это тоже подтранзакция

Часто подтранзакции появляются неявно. Блок BEGIN ... EXCEPTION ... END в PL/pgSQL оборачивается в подтранзакцию: если внутри возникает ошибка, откатывается именно подтранзакция, а обработчик EXCEPTION ловит сбой, и основная транзакция продолжается. Без подтранзакции любая ошибка губила бы всю транзакцию целиком.

Подводный камень: переполнение кэша подтранзакций

У каждого бэкенда есть небольшой кэш на 64 активных subxid. Если в одной транзакции открыть больше (тысячи savepoints или вложенных блоков EXCEPTION в цикле), кэш переполняется. Тогда проверка видимости вынуждена ходить в pg_subtrans за каждым subxid, и это заметно тормозит всю систему, а не только виновника. Поэтому массовые savepoints в горячем цикле - это антипаттерн, даже если логически они корректны.

§ команды

bash
SAVEPOINT s1;

Открыть точку сохранения (подтранзакцию) внутри транзакции

bash
ROLLBACK TO SAVEPOINT s1;

Откатить работу после точки, не завершая транзакцию

bash
RELEASE SAVEPOINT s1;

Закрыть точку сохранения, оставив её изменения в транзакции

§ см. также

  • clog-hint-bitsclog и подсказки фиксации (hint bits)Зафиксирована транзакция или откатилась, хранит clog (каталог pg_xact) - всего два бита на транзакцию. Чтобы не дёргать clog при каждом чтении строки, результат проверки кэшируется в самой строке подсказками фиксации в t_infomask. Первый прочитавший строку ставит подсказку - и тем самым пачкает страницу, даже если это был обычный SELECT.
  • xmin-xmaxxmin, xmax и правила видимостиУ каждой версии строки два транзакционных штампа: xmin (кто вставил) и xmax (кто пометил устаревшей, 0 - если жива). Версия видна транзакции, если её xmin уже зафиксирован и попадает в снимок, а xmax либо пуст, либо ещё не зафиксирован, либо не виден снимку. По этим двум числам строится вся видимость.
  • virtual-xidВиртуальные и реальные xidКаждая транзакция сразу получает виртуальный xid - дешёвую пару «бэкенд/счётчик», которая ничего не расходует. Настоящий 32-битный xid выдаётся только когда транзакция впервые что-то пишет. Поэтому read-only транзакции не тратят xid вовсе - это бережёт ограниченное пространство счётчика и отдаляет wraparound.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки