Чтобы посмотреть, как видимость определяется снимком, воспроизведем ситуацию, при которой в таблице будут три строки, причем:
=> CREATE DATABASE mvcc_snapshots;
CREATE DATABASE
=> \c mvcc_snapshots
You are now connected to database "mvcc_snapshots" as user "postgres".
=> CREATE TABLE t(s text);
CREATE TABLE
Первая транзакция (еще не завершилась):
=> BEGIN;
BEGIN
=> INSERT INTO t VALUES ('first');
INSERT 0 1
=> SELECT txid_current();
txid_current -------------- 583 (1 row)
Вторая транзакция:
=> \c mvcc_snapshots
You are now connected to database "mvcc_snapshots" as user "postgres".
=> BEGIN;
BEGIN
=> INSERT INTO t VALUES ('second');
INSERT 0 1
=> SELECT txid_current();
txid_current -------------- 584 (1 row)
=> COMMIT;
COMMIT
Создаем снимок в транзакции в другом сеансе.
=> \c mvcc_snapshots
You are now connected to database "mvcc_snapshots" as user "postgres".
=> BEGIN ISOLATION LEVEL REPEATABLE READ;
BEGIN
Вот что видно в снимке:
=> SELECT * FROM t;
s -------- second (1 row)
Заметим, что эта транзакция не меняет данные. Если вызвать функцию txid_current, как мы делали раньше, она выделит номер и вернет его. Более честной является другая функция, доступная с версии PostgreSQL 10:
=> SELECT txid_current_if_assigned();
txid_current_if_assigned -------------------------- (1 row)
Пустое значение говорит о том, что настоящий номер еще не выделен.
Завершаем первую транзакцию после того, как создан снимок:
=> COMMIT;
COMMIT
Третья транзакция:
=> BEGIN;
BEGIN
=> INSERT INTO t VALUES ('third');
INSERT 0 1
=> SELECT txid_current();
txid_current -------------- 585 (1 row)
=> COMMIT;
COMMIT
Очевидно, что в нашем снимке по-прежнему видна одна строка:
=> SELECT *, xmin, xmax FROM t;
s | xmin | xmax --------+------+------ second | 584 | 0 (1 row)
Вопрос в том, как это понимает PostgreSQL.
Все определяется снимком. Посмотрим на него:
=> select txid_current_snapshot();
txid_current_snapshot ----------------------- 583:585:583 (1 row)
Снимок отображается как три значения, разделенные двоеточиями:
Таким образом, в снимке должны быть видны изменения транзакций с номерами:
=> SELECT *, xmin, xmax FROM t;
s | xmin | xmax --------+------+------ first | 583 | 0 second | 584 | 0 third | 585 | 0 (3 rows)
Первая строка не видна - она создана транзакцией 583, которая входит в список активных.
Вторая строка видна - она создана транзакцией 584, которая попадает в диапазон снимка.
Третья строка не видна - она создана транзакцией 585, которая не входит в диапазон снимка.
Горизонт событий транзакции можно увидеть в системном каталоге:
=> SELECT backend_xmin FROM pg_stat_activity WHERE pid = pg_backend_pid();
backend_xmin -------------- 583 (1 row)
После завершения транзакции горизонт продвигается вперед, позволяя очищать неактуальные версии строк:
=> COMMIT;
COMMIT
=> SELECT backend_xmin FROM pg_stat_activity WHERE pid = pg_backend_pid();
backend_xmin -------------- 586 (1 row)
Конец демонстрации.