Jak dokładnie określa się widoczność rzędu?

10

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 FALSEprzez 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 xmaxjest 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 vistabeli - zauważ, że pageinspectpokazuje 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 UPDATEw 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 UPDATEwyniki 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 clogjest 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? clogWe wcześniejszych wersjach Postgresa wspomniano o korupcji oraz podpowiedź, że można ręcznie zbudować fałszywy plik. Ta informacja bardzo by pomogła.
dezso
źródło

Odpowiedzi:

6

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ń, inaczej zatkany.

Drugi ma HEAP_XMAX_INVALID. Oznacza to, że nie musi konsultować się z blokadą, ponieważ ktoś już to zrobił, widział, że xmaxjest przerywany, i ustawił „bit podpowiedzi”, aby przyszłe procesy nie musiały ponownie odwiedzać blokady w tym wierszu.

Które flagi (lub kombinacja flag) każą systemowi odwiedzić chodak?

Jeśli nie ma heap_xmin_committedlub heap_xmin_invalid, musisz odwiedzić chodaka, aby zobaczyć, jaka była dyspozycja xmin. Jeśli transakcja jest nadal w toku, wiersz nie jest dla Ciebie widoczny i nie możesz ustawić żadnych flag. Jeśli transakcja została zatwierdzona lub wycofana, ustaw ją odpowiednio heap_xmin_committedlub heap_xmin_invalid(jeśli jest to wygodne - nie jest to obowiązkowe), aby przyszli ludzie nie musieli jej sprawdzać.

Jeśli xminjest ważny i zatwierdzony, a jeśli xmaxnie jest równy zero, a nie ma go heap_max_committedlub heap_max_invalid, to musisz odwiedzić chodaka, aby zobaczyć, jakie było dyspozycji tej transakcji.

Czy istnieje sposób, aby sprawdzić, co jest w chodaku? We wcześniejszych wersjach Postgresa wspomniano o korupcji zatykania oraz podpowiedź, że można ręcznie zbudować fałszywy plik. Ta informacja bardzo by pomogła.

Nie wiem, jak to zrobić w przyjazny dla użytkownika sposób. Możesz użyć „od”, aby zrzucić pliki zatykające w odpowiedni sposób, aby je sprawdzić i dowiedzieć się, gdzie sprawdzić za pomocą makr zdefiniowanych wsrc/backend/access/transam/clog.c

Dziwię się, że nie ma żadnych rozszerzeń w PGXN, które działałyby dla ciebie, ale nie mogłem ich znaleźć. Ale myślę, że to nie byłoby tak przydatne, ponieważ naprawdę musisz być w stanie to zrobić, gdy serwer nie jest uruchomiony.

jjanes
źródło
4

Spójrz na implementację HeapTupleSatisfiesMVCC () : faktyczna clogkontrola odbywa się w TransactionIdDidCommit () , ale jest wywoływana tylko wtedy, gdy nie można wywnioskować statusu transakcji z bitów infomask (makro HeapTupleHeaderXminCommitted () i przyjaciele).

Mam prześledzić wstecz do dostępu pg_clogdo funkcji TransactionDidCommit()i TransactionDidAbort(), następnie szukałem tam, gdzie są one nazywane i jedyne miejsce w kodzie związanej z pytaniem wydaje się być HeapTupleSatisfiesMVCC(). Z kodu tej funkcji można zobaczyć, że faktyczne wyszukiwanie zatkania może nastąpić tylko wtedy, gdy krotka nie ma ustawionych powiązanych bitów maski informacyjnej: kod zaczyna się od sprawdzenia bitów za pomocą HeapTupleHeaderXminCommitted()et al. Wyszukiwanie zatkania ma miejsce tylko wtedy, gdy bity nie są ustawione.

alex
źródło