У найпростішому випадку, коли ми вставимо новий рядок у таблицю (і транзакція здійсниться), це буде видно для всіх наступних транзакцій. Дивіться xmax
0 у цьому прикладі:
CREATE TABLE vis (
id serial,
is_active boolean
);
INSERT INTO vis (is_active) VALUES (FALSE);
SELECT ctid, xmin, xmax, * FROM vis;
ctid │xmin │ xmax │ id │ is_active
───────┼─────┼──────┼────┼───────────
(0,1) │2699 │ 0 │ 1 │ f
Коли ми його оновлюємо (оскільки прапор встановлено FALSE
випадково), він дещо змінюється:
UPDATE vis SET is_active = TRUE;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
──────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 0 │ 1 │ t
Згідно з моделлю MVCC, яку використовує PostgreSQL, було записано новий фізичний рядок, а старий - недійсний (це можна побачити з розділу ctid
). Новий все ще видно для всіх наступних транзакцій.
Тепер відбувається цікава річ, коли ми повертаємо назад UPDATE
:
BEGIN;
UPDATE vis SET is_active = TRUE;
ROLLBACK;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
───────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 2702 │ 1 │ t
Версія рядка залишається такою ж, але тепер xmax
встановлена на щось. Незважаючи на це, наступні транзакції можуть бачити цей (інакше незмінний) рядок.
Прочитавши трохи про це, ви можете з’ясувати кілька речей про видимість рядків. Існує карта видимості , але вона вказує лише на те, що видно цілу сторінку - вона точно не працює на рівні рядка (кортежу). Тоді є журнал фіксування (ака clog
) - але як Postgres з'ясовує, чи потрібно його відвідувати?
Я вирішив подивитися на шматочки інформації, щоб зрозуміти, як видимість працює насправді. Щоб побачити їх, найпростіший спосіб - це розширення pageinspect . Для того, щоб дізнатися, які біти встановлені, я створив таблицю для їх зберігання:
CREATE TABLE infomask (
i_flag text,
i_bits bit(16)
);
INSERT INTO infomask
VALUES
('HEAP_HASNULL', x'0001'::bit(16)),
('HEAP_HASVARWIDTH', x'0002'::bit(16)),
('HEAP_HASEXTERNAL', x'0004'::bit(16)),
('HEAP_HASOID', x'0008'::bit(16)),
('HEAP_XMAX_KEYSHR_LOCK', x'0010'::bit(16)),
('HEAP_COMBOCID', x'0020'::bit(16)),
('HEAP_XMAX_EXCL_LOCK', x'0040'::bit(16)),
('HEAP_XMAX_LOCK_ONLY', x'0080'::bit(16)),
('HEAP_XMIN_COMMITTED', x'0100'::bit(16)),
('HEAP_XMIN_INVALID', x'0200'::bit(16)),
('HEAP_XMAX_COMMITTED', x'0400'::bit(16)),
('HEAP_XMAX_INVALID', x'0800'::bit(16)),
('HEAP_XMAX_IS_MULTI', x'1000'::bit(16)),
('HEAP_UPDATED', x'2000'::bit(16)),
('HEAP_MOVED_OFF', x'4000'::bit(16)),
('HEAP_MOVED_IN', x'8000'::bit(16)),
('HEAP_XACT_MASK', x'FFF0'::bit(16));
Потім перевірте, що знаходиться всередині моєї vis
таблиці - зверніть увагу, що pageinspect
показує фізичний вміст купи, тому не тільки видимі рядки повертаються:
SELECT t_xmin, t_xmax, string_agg(i_flag, ', ') FILTER (WHERE (t_infomask::bit(16) & i_bits)::integer::boolean)
FROM heap_page_items(get_raw_page('vis', 0)),
infomask
GROUP BY t_xmin, t_xmax;
t_xmin │ t_xmax │ string_agg
────────┼────────┼──────────────────────────────────────────────────────
2699 │ 2700 │ HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
2700 │ 2702 │ HEAP_XMIN_COMMITTED, HEAP_XMAX_INVALID, HEAP_UPDATED
2702 │ 0 │ HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED
Що я розумію з вищесказаного, це те, що перша версія ожила трансакцією 2699, потім її успішно замінили новою версією в 2700.
Потім наступну, яка була жива з 2700 року, була спроба UPDATE
відкоту в 2702 році, видно з HEAP_XMAX_INVALID
.
Останній ніколи насправді не народився, як показав HEAP_XMIN_INVALID
.
Отже, здогадуючись з вищесказаного, перший та останній випадок очевидні - вони вже не видно для транзакції 2703 або вище.
Другий слід десь шукати - я вважаю, що це журнал фіксування, ака clog
.
Щоб ще більше ускладнити проблеми, подальшим UPDATE
результатом є наступне:
t_xmin │ t_xmax │ string_agg
────────┼────────┼────────────────────────────────────────────────────
2699 │ 2700 │ HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
2702 │ 0 │ HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED
2703 │ 0 │ HEAP_XMAX_INVALID, HEAP_UPDATED
2700 │ 2703 │ HEAP_XMIN_COMMITTED, HEAP_UPDATED
Тут я бачу вже двох кандидатів, які могли бути видні. Отже, нарешті, ось мої запитання:
- Чи моє припущення, що саме
clog
тут слід подивитися, щоб визначити видимість у цих випадках? - Які прапори (або комбінації прапорів) повідомляють системі відвідувати
clog
? - Чи є спосіб вивчити, що знаходиться всередині
clog
? Уclog
попередніх версіях Postgres згадується корупція та натяк на те, що можна створити підроблений файл вручну. Ця інформація допоможе нам у цьому.