lesson ── postgres-labs ── ~22 мин ── 5 шагов
Собери цепочку версий руками и декодируй заголовки. Перед запросом heap_page_items предсказывай: сколько версий будет на странице, какой ctid у живой, как свяжутся xmax и xmin соседних версий.
интерактивный sandbox
Поднимется контейнер postgreslab/postgres-base с PostgreSQL 17 и psql. В браузере откроется терминал, база lab уже настроена. Каждый шаг проверяется автоматически. Сеть air-gapped, наружу контейнер не ходит.
stack ── PostgreSQL 17 · psql · 1 GB RAM · air-gapped · самоуничтожается через 45 мин простоя
Создай таблицу, вставь строку и дважды обнови её. Получится три версии на одной странице. Предскажи их число.
CREATE TABLE tup (id int, note text);
INSERT INTO tup VALUES (1, 'v1');
UPDATE tup SET note = 'v2' WHERE id = 1;
UPDATE tup SET note = 'v3' WHERE id = 1;
SELECT count(*) FROM heap_page_items(get_raw_page('tup', 0));Каждый UPDATE добавляет новую версию, старая остаётся на странице.
✓ Три версии на странице: одна живая, две устаревшие.
SELECT показывает только живую версию. Предскажи её ctid до запроса.
SELECT ctid, xmin, xmax, note FROM tup;
Третья по счёту версия - последняя в цепочке.
✓ Живая версия - (0,3), последняя в цепочке.
Живая версия ещё никем не помечена устаревшей, поэтому её xmax - ноль.
SELECT xmax FROM tup;
✓ xmax = 0 - версия жива.
Прочитай все версии и проверь связь: транзакция, закрывшая старую версию, открыла новую, поэтому xmax старой совпадает с xmin новой.
SELECT lp, t_xmin, t_xmax, t_ctid
FROM heap_page_items(get_raw_page('tup', 0)) ORDER BY lp;✓ xmax первой версии равен xmin второй - одна транзакция.
У первой версии t_ctid указывает не на себя, а на вторую версию (0,2). Так образуется цепочка (0,1) → (0,2) → (0,3).
SELECT lp, t_ctid FROM heap_page_items(get_raw_page('tup', 0)) ORDER BY lp;✓ t_ctid первой версии ведёт на (0,2) - звено цепочки.
UPDATE строит цепочку версий: t_ctid ведёт от старой к новой, xmax каждой равен xmin следующей, живая версия имеет xmax 0 и указывает на себя. SELECT показывает только живую, устаревшие лежат на странице.
команды
SELECT ctid, xmin, xmax FROM t;версионные поля живой строкиSELECT lp, t_xmin, t_xmax, t_ctid FROM heap_page_items(get_raw_page('t',0));все версии на страницеконцепции