W najprostszym przypadku, gdy wstawimy nowy wiersz do tabeli (i transakcja zostanie zatwierdzona), będzie ona widoczna dla wszystkich kolejnych transakcji. Zobacz xmax
, czy w tym przykładzie jest 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
Kiedy ją aktualizujemy (ponieważ flaga została ustawiona FALSE
przez przypadek), zmienia się ona nieco:
UPDATE vis SET is_active = TRUE;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
──────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 0 │ 1 │ t
Zgodnie z modelem MVCC, z którego korzysta PostgreSQL, zapisano nowy wiersz fizyczny, a stary unieważniono (można to zobaczyć z ctid
). Nowa jest nadal widoczna dla wszystkich kolejnych transakcji.
Teraz, gdy cofamy 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
Wersja wiersza pozostaje taka sama, ale teraz xmax
jest ustawiona na coś. Mimo to kolejne transakcje mogą zobaczyć ten (w przeciwnym razie niezmieniony) wiersz.
Po przeczytaniu trochę na ten temat możesz dowiedzieć się kilku rzeczy na temat widoczności wiersza. Istnieje mapa widoczności , ale mówi tylko, czy cała strona jest widoczna - zdecydowanie nie działa na poziomie wiersza (krotki). Następnie jest dziennik zatwierdzeń (aka clog
) - ale jak Postgres wie, czy musi go odwiedzić?
Postanowiłem rzucić okiem na bity informacyjne, aby dowiedzieć się, jak działa widoczność. Aby je zobaczyć, najprostszym sposobem jest użycie rozszerzenia pageinspect . Aby dowiedzieć się, które bity są ustawione, utworzyłem tabelę do ich przechowywania:
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));
Następnie sprawdziłem, co jest w mojej vis
tabeli - zauważ, że pageinspect
pokazuje fizyczną zawartość sterty, więc zwracane są nie tylko widoczne wiersze:
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
Rozumiem z powyższego, że pierwsza wersja ożyła z transakcją 2699, a następnie z powodzeniem zastąpiła ją nową wersją o 2700.
Następnie następna, działająca od 2700 r., Miała próbę wycofania UPDATE
w roku 2702, widzianą z HEAP_XMAX_INVALID
.
Ostatni nigdy się tak naprawdę nie narodził, jak pokazuje HEAP_XMIN_INVALID
.
Zgadując z powyższego, pierwszy i ostatni przypadek są oczywiste - nie są już widoczne dla transakcji 2703 lub wyższej.
Drugi trzeba gdzieś odszukać - przypuszczam, że jest to dziennik zatwierdzeń, alias clog
.
Aby dodatkowo skomplikować problemy, kolejne UPDATE
wyniki są następujące:
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
Tutaj widzę już dwóch kandydatów, którzy mogliby być widoczni. Oto moje pytania:
- Czy moje założenie, że
clog
jest to miejsce, w którym można sprawdzić widoczność w tych przypadkach? - Które flagi (lub kombinacja flag) każą systemowi odwiedzić
clog
? - Czy istnieje sposób na sprawdzenie, co jest w środku
clog
?clog
We wcześniejszych wersjach Postgresa wspomniano o korupcji oraz podpowiedź, że można ręcznie zbudować fałszywy plik. Ta informacja bardzo by pomogła.