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 и видимость/xmin-xmax

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

xmin, xmax и правила видимости

У каждой версии строки два транзакционных штампа: xmin (кто вставил) и xmax (кто пометил устаревшей, 0 - если жива). Версия видна транзакции, если её xmin уже зафиксирован и попадает в снимок, а xmax либо пуст, либо ещё не зафиксирован, либо не виден снимку. По этим двум числам строится вся видимость.

view as markdownaka: visibility-rules, tuple-visibility

Видимость строки в PostgreSQL - это не флаг «удалена / не удалена», а вывод из двух чисел. Каждая версия (см. tuple-header) хранит:

  • xmin - идентификатор транзакции, которая её вставила;
  • xmax - идентификатор транзакции, которая её пометила устаревшей (удалила, обновила или заблокировала). Если версия жива, xmax = 0.

Прочитать их можно прямо как системные колонки:

sql
SELECT xmin, xmax, ctid, * FROM mv;
--  xmin | xmax | ctid  | id | note
-- ------+------+-------+----+------
--   786 |    0 | (0,1) |  1 | a

Правило видимости

Транзакция смотрит на версию через свой снимок (см. snapshot) и решает по двум вопросам:

  1. Появилась ли версия в прошлом? xmin должен быть зафиксирован и виден снимку: транзакция-создатель уже закоммитилась и не идёт прямо сейчас.
  2. Не исчезла ли версия в прошлом? xmax должен быть либо 0, либо указывать на транзакцию, которая ещё не зафиксирована или не видна снимку. Тогда удаление «ещё не случилось» с точки зрения этой транзакции.

Если оба условия выполнены - версия видна. Коротко: видно то, что уже вставлено и ещё не удалено в твоём срезе времени.

Откуда берётся статус «зафиксирован»

Сами по себе xmin и xmax - просто номера. Зафиксирована транзакция или откатилась, хранится отдельно, в clog. При первой проверке результат кэшируется в строке подсказками фиксации, чтобы не лазить в clog каждый раз. Это разбирает clog-hint-bits.

xmax не всегда значит «удалена»

Тонкость: xmax ставится не только при удалении, но и когда строку просто блокируют - например, SELECT ... FOR UPDATE или вставка дочерней строки с внешним ключом. Такой xmax помечен как «только блокировка», и строка остаётся живой. Поэтому ненулевой xmax у видимой строки - не обязательно ошибка:

sql
-- у строк flights xmax заполнен: их «придержала» вставка в tickets по FK,
-- но сами строки живы
SELECT xmin, xmax FROM flights LIMIT 1;

Зачем это знать

Правило видимости - это фундамент, на котором стоят уровни изоляции (см. isolation-levels). Один и тот же набор версий на диске разные транзакции видят по-разному, потому что у каждой свой снимок, а правило одно. Когда понимаешь, что видимость вычисляется, а не хранится, перестают удивлять и «пропавшие» в одной сессии строки, и мёртвые версии, которые держит долгая транзакция (см. transaction-horizon).

§ команды

bash
SELECT xmin, xmax, ctid, * FROM mv;

Транзакционные штампы каждой видимой версии строки

bash
SELECT t_xmin, t_xmax, t_infomask::bit(16) FROM heap_page_items(get_raw_page('mv', 0));

Те же поля на уровне страницы плюс флаги (в т.ч. блокировка vs удаление)

bash
SELECT pg_current_snapshot();

Текущий снимок - рамка, относительно которой решается видимость

§ см. также

  • snapshotСнимок данных (snapshot)Снимок - это не копия данных, а три числа: xmin, xmax и список активных xid между ними. Плюс правило, как по ним решать видимость версии. По снимку транзакция отделяет «прошлое» (зафиксировано до неё) от «настоящего» (идёт прямо сейчас) и «будущего» (ещё не начато). Дёшево по памяти, дорого по числу соединений.
  • tuple-headerЗаголовок кортежаПеред данными каждой строки лежит служебный заголовок в 23 байта, выровненный до 24. В нём t_xmin и t_xmax (кто вставил и кто удалил версию), t_ctid (ссылка на себя или на новую версию), t_infomask с флагами и t_hoff - смещение до пользовательских данных. Это поля, на которых стоит весь MVCC.
  • clog-hint-bitsclog и подсказки фиксации (hint bits)Зафиксирована транзакция или откатилась, хранит clog (каталог pg_xact) - всего два бита на транзакцию. Чтобы не дёргать clog при каждом чтении строки, результат проверки кэшируется в самой строке подсказками фиксации в t_infomask. Первый прочитавший строку ставит подсказку - и тем самым пачкает страницу, даже если это был обычный SELECT.
  • isolation-levelsУровни изоляции в PostgreSQLPostgreSQL различает три уровня изоляции. Read Committed (по умолчанию) берёт свежий снимок на каждый оператор. Repeatable Read берёт один снимок на транзакцию и держит его до конца. Serializable добавляет проверку, что транзакции можно выстроить в строгий порядок. Грязного чтения в PostgreSQL не бывает ни на одном уровне.
  • transaction-horizonГоризонт транзакцииГоризонт - минимум backend_xmin по всем активным транзакциям. Версию строки можно убрать, только если она стала мёртвой раньше горизонта. Одна долгая или idle-in-transaction транзакция отодвигает горизонт назад и запрещает уборку мусора во всей базе.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки