Nel caso più semplice, quando inseriamo una nuova riga in una tabella (e la transazione si impegna), sarà visibile a tutte le transazioni successive. Vedi xmax
essere 0 in questo esempio:
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
Quando lo aggiorniamo (perché la bandiera è stata impostata FALSE
accidentalmente), cambia un po ':
UPDATE vis SET is_active = TRUE;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
──────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 0 │ 1 │ t
Secondo il modello MVCC utilizzato da PostgreSQL, è stata scritta una nuova riga fisica e quella vecchia è stata invalidata (come si può vedere dal ctid
). Il nuovo è ancora visibile a tutte le transazioni successive.
Ora succede qualcosa di interessante quando eseguiamo il rollback di 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
La versione della riga rimane la stessa, ma ora xmax
è impostata su qualcosa. Nonostante ciò, le transazioni successive possono vedere questa riga (altrimenti invariata).
Dopo aver letto un po 'di questo, potresti capire alcune cose sulla visibilità delle righe. C'è la mappa di visibilità , ma dice solo se un'intera pagina è visibile - sicuramente non funziona a livello di riga (tupla). Poi c'è il registro di commit (aka clog
) - ma come fa Postgres a capire se deve visitarlo?
Ho deciso di dare un'occhiata ai bit di infomask per capire come funziona effettivamente la visibilità. Per vederli, il modo più semplice è usare l' estensione pageinspect . Per scoprire quali bit sono impostati, ho creato una tabella per memorizzarli:
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));
Quindi ho controllato cosa c'è dentro la mia vis
tabella - nota che pageinspect
mostra il contenuto fisico dell'heap, quindi non vengono restituite solo le righe visibili:
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
Ciò che capisco da quanto sopra è che la prima versione prese vita con la transazione 2699, poi sostituita con successo dalla nuova versione a 2700.
Quindi la successiva, che era in vita dal 2700, ebbe un tentativo di rollback UPDATE
nel 2702, visto da HEAP_XMAX_INVALID
.
L'ultimo non è mai nato davvero, come dimostrato da HEAP_XMIN_INVALID
.
Quindi, indovinando da quanto sopra, il primo e l'ultimo caso sono ovvi: non sono più visibili alla transazione 2703 o successiva.
Il secondo deve essere cercato da qualche parte - suppongo che sia il registro di commit, aka clog
.
Per complicare ulteriormente i problemi, un conseguente UPDATE
risultato nel seguente:
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
Qui vedo già due candidati che potrebbero essere visibili. Quindi, finalmente, ecco le mie domande:
- La mia ipotesi è che
clog
sia il posto giusto per determinare la visibilità in questi casi? - Quali flag (o combinazioni di flag) indicano al sistema di visitare il
clog
? - C'è un modo per esaminare cosa c'è dentro
clog
? Ci sono menzioni sullaclog
corruzione nelle versioni precedenti di Postgres e un suggerimento che si può creare manualmente un file falso. Questa informazione sarebbe di grande aiuto.