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

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

Виртуальные и реальные xid

Каждая транзакция сразу получает виртуальный xid - дешёвую пару «бэкенд/счётчик», которая ничего не расходует. Настоящий 32-битный xid выдаётся только когда транзакция впервые что-то пишет. Поэтому read-only транзакции не тратят xid вовсе - это бережёт ограниченное пространство счётчика и отдаляет wraparound.

view as markdownaka: vxid, virtual-transaction-id

Настоящих идентификаторов транзакций (xid) ограниченное число: счётчик 32-битный, около четырёх миллиардов значений, и он по кругу переиспользуется. Если бы каждая транзакция, включая голые SELECT, сжигала по xid, счётчик летел бы вперёд и заморозка с борьбой за wraparound случались бы куда чаще.

PostgreSQL экономит. Транзакция получает реальный xid не сразу, а только по необходимости.

Два идентификатора

ИдентификаторКогда выдаётсяЧего стоит
виртуальный (vxid)при старте любой транзакцииничего, переиспользуется
реальный (xid)при первой записирасходует счётчик транзакций

Виртуальный xid - это пара «номер бэкенда / локальный счётчик», например 3/12. Он выдаётся мгновенно и бесплатно, живёт только пока открыта транзакция, и после неё номер свободно переиспользуется. Глобальный счётчик транзакций он не двигает.

Реальный xid нужен, только когда транзакция собирается оставить след в данных: вставить, обновить или удалить строку. Тогда ей нужно проштамповать версии своим xmin/xmax (см. xmin-xmax), а для этого требуется настоящий номер.

Увидеть разницу

sql
BEGIN;
SELECT pg_current_xact_id_if_assigned();   -- пусто: только читали, xid не выдан
INSERT INTO mv VALUES (9, 'write');
SELECT pg_current_xact_id_if_assigned();   -- теперь число: запись потребовала xid
COMMIT;

Пока транзакция ничего не записала, pg_current_xact_id_if_assigned() возвращает пусто - реального xid у неё нет. Первая же запись его назначает. Функция pg_current_xact_id() назначила бы xid принудительно, поэтому в диагностике берут вариант ..._if_assigned, чтобы случайно не сжечь номер.

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

Из этого следуют две практичные вещи. Первая: длинная читающая транзакция не двигает счётчик и сама по себе не приближает wraparound (хотя горизонт всё равно держит - но это про vacuum). Вторая: в pg_locks у транзакции всегда есть строка virtualxid, а строка transactionid появляется только после первой записи. Поэтому по наличию реального xid можно понять, писала транзакция или только читала.

§ команды

bash
SELECT pg_current_xact_id_if_assigned();

Реальный xid, если уже выдан; пусто - транзакция только читала

bash
SELECT virtualtransaction, transactionid FROM pg_locks WHERE pid = pg_backend_pid();

Виртуальный xid есть всегда, реальный - только после записи

bash
SELECT backend_xid, backend_xmin FROM pg_stat_activity WHERE pid = pg_backend_pid();

backend_xid пуст у читающей транзакции, backend_xmin держит горизонт

§ см. также

  • xmin-xmaxxmin, xmax и правила видимостиУ каждой версии строки два транзакционных штампа: xmin (кто вставил) и xmax (кто пометил устаревшей, 0 - если жива). Версия видна транзакции, если её xmin уже зафиксирован и попадает в снимок, а xmax либо пуст, либо ещё не зафиксирован, либо не виден снимку. По этим двум числам строится вся видимость.
  • snapshotСнимок данных (snapshot)Снимок - это не копия данных, а три числа: xmin, xmax и список активных xid между ними. Плюс правило, как по ним решать видимость версии. По снимку транзакция отделяет «прошлое» (зафиксировано до неё) от «настоящего» (идёт прямо сейчас) и «будущего» (ещё не начато). Дёшево по памяти, дорого по числу соединений.
  • subtransactionsВложенные транзакции и savepointsSAVEPOINT открывает вложенную транзакцию (подтранзакцию) внутри основной. ROLLBACK TO SAVEPOINT откатывает её работу, не трогая остальную транзакцию. Каждая пишущая подтранзакция получает свой xid, а связь «подтранзакция - родитель» хранит pg_subtrans. Блоки EXCEPTION в PL/pgSQL - это тоже подтранзакции.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки