Come viene determinata esattamente la visibilità delle righe?


10

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 xmaxessere 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 FALSEaccidentalmente), 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 vistabella - nota che pageinspectmostra 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 UPDATEnel 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 UPDATErisultato 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 clogsia 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 sulla clogcorruzione nelle versioni precedenti di Postgres e un suggerimento che si può creare manualmente un file falso. Questa informazione sarebbe di grande aiuto.

Risposte:


6

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, noto anche come clog.

Il secondo ha HEAP_XMAX_INVALID. Ciò significa che non è necessario consultare l'intasamento, perché qualcuno lo ha già fatto, visto che xmaxè stato interrotto e impostare un "suggerimento" in modo che i processi futuri non debbano visitare nuovamente l'intasamento per quella riga.

Quali flag (o combinazioni di flag) indicano al sistema di visitare l'intasamento?

Se non c'è heap_xmin_committedo heap_xmin_invalid, allora devi visitare l'intasamento per vedere qual era la disposizione di xmin. Se la transazione è ancora in corso, la riga non è visibile e non puoi impostare alcun flag. Se la transazione è stata impegnata o ripristinata, si imposta heap_xmin_committedo di heap_xmin_invalidconseguenza (se è conveniente farlo - non è obbligatorio) in modo che le persone future non debbano cercarla.

Se xminè valido e impegnato, e se xmaxnon è zero, e non c'è heap_max_committedo heap_max_invalid, allora devi visitare l'intasamento per vedere quale fosse la disposizione di quella transazione.

C'è un modo per esaminare cosa c'è dentro lo zoccolo? Ci sono menzioni sulla ostruzione della corruzione nelle versioni precedenti di Postgres e un suggerimento che si può creare manualmente un file falso. Questa informazione sarebbe di grande aiuto.

Non sono a conoscenza di un modo intuitivo per farlo. Puoi usare "od" per scaricare i file di intasamento in modo adeguato per ispezionarli e capire dove ispezionare usando le macro definite insrc/backend/access/transam/clog.c

Sono sorpreso che non ci siano estensioni su PGXN che fa il lavoro per te, ma non sono riuscito a trovarne uno. Ma penso che non sarebbe molto utile, perché devi davvero essere in grado di farlo mentre il tuo server non è in esecuzione.


4

Dai un'occhiata all'implementazione di HeapTupleSatisfiesMVCC () : il clogcontrollo effettivo si verifica in TransactionIdDidCommit () , ma viene chiamato solo se lo stato della transazione non può essere dedotto dai bit infomask (macro HeapTupleHeaderXminCommitted () e amici).

Ho rintracciato l'accesso alle pg_clogfunzioni TransactionDidCommit()e TransactionDidAbort(), quindi, ho cercato dove vengono chiamate e sembra che si trovi l'unico posto nel codice relativo alla tua domanda HeapTupleSatisfiesMVCC(). Dal codice di questa funzione si può vedere che la vera ricerca di clog può avvenire solo se la tupla non ha i relativi set di bit infomask: il codice inizia controllando i bit con HeapTupleHeaderXminCommitted()et al. E la ricerca di intasamento avviene solo se i bit non sono impostati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.