Снимки данных

Чтобы посмотреть, как видимость определяется снимком, воспроизведем ситуацию, при которой в таблице будут три строки, причем:

=> 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)

Конец демонстрации.