Видимость строки в PostgreSQL - это не флаг «удалена / не удалена», а вывод из двух чисел. Каждая версия (см. tuple-header) хранит:
xmin- идентификатор транзакции, которая её вставила;xmax- идентификатор транзакции, которая её пометила устаревшей (удалила, обновила или заблокировала). Если версия жива,xmax = 0.
Прочитать их можно прямо как системные колонки:
SELECT xmin, xmax, ctid, * FROM mv;
-- xmin | xmax | ctid | id | note
-- ------+------+-------+----+------
-- 786 | 0 | (0,1) | 1 | a
Правило видимости
Транзакция смотрит на версию через свой снимок (см. snapshot) и решает по двум вопросам:
- Появилась ли версия в прошлом?
xminдолжен быть зафиксирован и виден снимку: транзакция-создатель уже закоммитилась и не идёт прямо сейчас. - Не исчезла ли версия в прошлом?
xmaxдолжен быть либо 0, либо указывать на транзакцию, которая ещё не зафиксирована или не видна снимку. Тогда удаление «ещё не случилось» с точки зрения этой транзакции.
Если оба условия выполнены - версия видна. Коротко: видно то, что уже вставлено и ещё не удалено в твоём срезе времени.
Откуда берётся статус «зафиксирован»
Сами по себе xmin и xmax - просто номера. Зафиксирована транзакция или
откатилась, хранится отдельно, в clog. При первой проверке результат
кэшируется в строке подсказками фиксации, чтобы не лазить в clog каждый
раз. Это разбирает clog-hint-bits.
xmax не всегда значит «удалена»
Тонкость: xmax ставится не только при удалении, но и когда строку просто
блокируют - например, SELECT ... FOR UPDATE или вставка дочерней
строки с внешним ключом. Такой xmax помечен как «только блокировка», и
строка остаётся живой. Поэтому ненулевой xmax у видимой строки - не
обязательно ошибка:
-- у строк flights xmax заполнен: их «придержала» вставка в tickets по FK,
-- но сами строки живы
SELECT xmin, xmax FROM flights LIMIT 1;
Зачем это знать
Правило видимости - это фундамент, на котором стоят уровни изоляции (см. isolation-levels). Один и тот же набор версий на диске разные транзакции видят по-разному, потому что у каждой свой снимок, а правило одно. Когда понимаешь, что видимость вычисляется, а не хранится, перестают удивлять и «пропавшие» в одной сессии строки, и мёртвые версии, которые держит долгая транзакция (см. transaction-horizon).