В простейшем случае, когда мы вставляем новую строку в таблицу (и транзакция фиксируется), она будет видна всем последующим транзакциям. Смотрите 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 есть упоминания о коррупции и подсказка, что можно создать поддельный файл вручную. Эта часть информации очень поможет в этом.