MVCC-3. Versioni di stringa

Dunque, avemu cunsideratu questioni ligati à insulation, è hà fattu una ritirata circa urganizazione di dati à un livellu bassu. È infine avemu ghjuntu à a parte più interessante - e versioni di stringa.

Header

Comu avemu digià dettu, ogni fila pò esiste simultaneamente in parechje versioni in a basa di dati. Una versione deve esse distinta da una altra.Per questu scopu, ogni versione hà duie marche chì determinanu u "tempu" di l'azzione di sta versione (xmin è xmax). In quotes - perchè ùn hè micca u tempu cum'è tali chì hè utilizatu, ma un contatore crescente speciale. È questu contatore hè u numeru di transazzione.

(Cum'è solitu, a realità hè più cumplicata: u numeru di transazzione ùn pò micca aumentà tuttu u tempu per via di a capacità limitata di bit di u cuntatore. Ma vedemu sti ditaglii in dettagliu quandu ghjunghjemu à a congelazione.)

Quandu una fila hè creata, xmin hè stabilitu à u numeru di transazzione chì hà emessu u cumandimu INSERT, è xmax hè lasciatu in biancu.

Quandu una fila hè sguassata, u valore xmax di a versione attuale hè marcatu cù u numeru di a transazzione chì hà realizatu a DELETE.

Quandu una fila hè mudificata da un cumandamentu UPDATE, duie operazioni sò veramente realizate: DELETE è INSERT. A versione attuale di a fila stabilisce xmax uguali à u numeru di a transazzione chì hà realizatu l'UPDATE. Una nova versione di a listessa stringa hè tandu creata; u so valore xmin coincide cù u valore xmax di a versione precedente.

I campi xmin è xmax sò inclusi in l'intestazione di a versione di fila. In più di sti campi, l'intestazione cuntene altri, per esempiu:

  • infomask hè una seria di bits chì definiscenu e pruprietà di sta versione. Ci sò assai di elli; Avemu da cunsiderà gradualmente i principali.
  • ctid hè un ligame à a prossima versione più nova di a stessa linea. Per a versione più nova, più attuale di una stringa, u ctid si riferisce à sta versione stessu. U numeru hà a forma (x,y), induve x hè u numeru di pagina, y hè u numeru indice in u array.
  • null bitmap - Marca quelle colonne di una versione data chì cuntenenu un valore nulu (NULL). NULL ùn hè micca unu di i valori normali di u tipu di dati, cusì l'attributu deve esse guardatu separatamente.

In u risultatu, l'intestazione hè abbastanza grande - almenu 23 bytes per ogni versione di a linea, è di solitu più per via di u bitmap NULL. Se a tavula hè "stretta" (vale à dì, cuntene uni pochi di culonni), l'overhead pò piglià più di l'infurmazioni utili.

inserisci

Fighjemu un ochju più vicinu à cumu l'operazioni di stringa di livellu bassu sò realizate, cuminciendu cù l'inserimentu.

Per l'esperimenti, creamu una nova tavula cù duie colonne è un indice nantu à una di elli:

=> CREATE TABLE t(
  id serial,
  s text
);
=> CREATE INDEX ON t(s);

Inseremu una fila dopu avè principiatu una transazzione.

=> BEGIN;
=> INSERT INTO t(s) VALUES ('FOO');

Eccu u nostru numeru di transazzione attuale:

=> SELECT txid_current();
 txid_current 
--------------
         3664
(1 row)

Fighjemu u cuntenutu di a pagina. A funzione heap_page_items di l'estensione pageinspect permette di ottene infurmazioni nantu à i puntatori è e versioni di fila:

=> SELECT * FROM heap_page_items(get_raw_page('t',0)) gx
-[ RECORD 1 ]-------------------
lp          | 1
lp_off      | 8160
lp_flags    | 1
lp_len      | 32
t_xmin      | 3664
t_xmax      | 0
t_field3    | 0
t_ctid      | (0,1)
t_infomask2 | 2
t_infomask  | 2050
t_hoff      | 24
t_bits      | 
t_oid       | 
t_data      | x0100000009464f4f

Nota chì a parolla heap in PostgreSQL si riferisce à e tavule. Questu hè un altru usu stranu di u terminu - un munzeddu hè cunnisciutu struttura di dati, chì ùn hà nunda in cumunu cù a tavula. Quì a parolla hè aduprata in u sensu di "tuttu hè ghjittatu inseme", in uppusizione à l'indici urdinati.

A funzione mostra dati "cum'è", in un furmatu chì hè difficiule di capiscenu. Per capisce, lasceremu solu una parte di l'infurmazioni è decifraremu:

=> SELECT '(0,'||lp||')' AS ctid,
       CASE lp_flags
         WHEN 0 THEN 'unused'
         WHEN 1 THEN 'normal'
         WHEN 2 THEN 'redirect to '||lp_off
         WHEN 3 THEN 'dead'
       END AS state,
       t_xmin as xmin,
       t_xmax as xmax,
       (t_infomask & 256) > 0  AS xmin_commited,
       (t_infomask & 512) > 0  AS xmin_aborted,
       (t_infomask & 1024) > 0 AS xmax_commited,
       (t_infomask & 2048) > 0 AS xmax_aborted,
       t_ctid
FROM heap_page_items(get_raw_page('t',0)) gx
-[ RECORD 1 ]-+-------
ctid          | (0,1)
state         | normal
xmin          | 3664
xmax          | 0
xmin_commited | f
xmin_aborted  | f
xmax_commited | f
xmax_aborted  | t
t_ctid        | (0,1)

Eccu ciò chì avemu fattu:

  • Aggiuntu un cero à u numaru d'indici per fà vede u listessu cum'è t_ctid: (numeru di pagina, numaru d'indici).
  • Decifratu u statu di u puntatore lp_flags. Quì hè "normale" - questu significa chì l'indicatore in realtà si riferisce à a versione di a stringa. Fighjeremu altri significati dopu.
  • Di tutti i bits d'infurmazioni, solu duie coppie sò state identificate finu à avà. I bits xmin_committed è xmin_aborted indicanu se u numeru di transazzione xmin hè impegnatu (abortu). Dui bits simili riferite à u numeru di transazzione xmax.

Chì vedemu ? Quandu inserite una fila, un nùmeru indici 1 appariscerà in a pagina di a tavula, indicà a prima è l'unica versione di a fila.

In a versione di stringa, u campu xmin hè cumpletu cù u numeru di transazzione attuale. A transazzione hè sempre attiva, cusì i bit xmin_committed è xmin_aborted ùn sò micca stabiliti.

U campu di a versione di fila ctid si riferisce à a stessa fila. Questu significa chì una versione più nova ùn esiste micca.

U campu xmax hè cumpletu cù un numeru manichino 0 perchè sta versione di a fila ùn hè micca stata sguassata è hè attuale. E transazzione ùn fate micca attente à questu numeru perchè u bit xmax_aborted hè stabilitu.

Facemu un passu più per migliurà a leggibilità aghjustendu bits d'infurmazioni à i numeri di transazzione. È creemu una funzione, postu chì avemu bisognu di a dumanda più di una volta:

=> CREATE FUNCTION heap_page(relname text, pageno integer)
RETURNS TABLE(ctid tid, state text, xmin text, xmax text, t_ctid tid)
AS $$
SELECT (pageno,lp)::text::tid AS ctid,
       CASE lp_flags
         WHEN 0 THEN 'unused'
         WHEN 1 THEN 'normal'
         WHEN 2 THEN 'redirect to '||lp_off
         WHEN 3 THEN 'dead'
       END AS state,
       t_xmin || CASE
         WHEN (t_infomask & 256) > 0 THEN ' (c)'
         WHEN (t_infomask & 512) > 0 THEN ' (a)'
         ELSE ''
       END AS xmin,
       t_xmax || CASE
         WHEN (t_infomask & 1024) > 0 THEN ' (c)'
         WHEN (t_infomask & 2048) > 0 THEN ' (a)'
         ELSE ''
       END AS xmax,
       t_ctid
FROM heap_page_items(get_raw_page(relname,pageno))
ORDER BY lp;
$$ LANGUAGE SQL;

In questa forma, hè assai più chjaru ciò chì succede in l'intestazione di a versione di fila:

=> SELECT * FROM heap_page('t',0);
 ctid  | state  | xmin | xmax  | t_ctid 
-------+--------+------+-------+--------
 (0,1) | normal | 3664 | 0 (a) | (0,1)
(1 row)

Simili, ma significativamente menu detallati, l'infurmazione pò esse acquistata da a tavula stessu, utilizendu pseudo-columns xmin è xmax:

=> SELECT xmin, xmax, * FROM t;
 xmin | xmax | id |  s  
------+------+----+-----
 3664 |    0 |  1 | FOO
(1 row)

Fissazione

Se una transazzione hè cumpleta cù successu, avete bisognu di ricurdà u so statutu - nota chì hè impegnatu. Per fà questu, una struttura chjamata XACT hè aduprata (è prima di a versione 10 si chjamava CLOG (commit log) è stu nome pò ancu esse truvatu in parechji lochi).

XACT ùn hè micca una tabella di catalogu di sistema; Quessi sò i schedarii in u cartulare PGDATA/pg_xact. Hanu dui bits attribuiti per ogni transazzione: cummessu è abortu - cum'è in l'intestazione di a versione di fila. Questa infurmazione hè divisa in parechji fugliali solu per comodità; vultaremu à questu prublema quandu pensemu a congelazione. È u travagliu cù questi schedari hè realizatu pagina per pagina, cum'è cù tutti l'altri.

Allora, quandu una transazzione hè impegnata in XACT, u bit impegnatu hè stabilitu per questa transazzione. È questu hè tuttu ciò chì succede durante a cummissione (ancu se ùn parlemu micca di u logu di pre-registrazione).

Quandu una altra transazzione accede à a pagina di a tavula chì avemu vistu solu, duverà risponde à parechje dumande.

  1. A transazzione xmin hè finita? Se no, allura a versione creata di a stringa ùn deve esse visibili.
    Stu cuntrollu hè realizatu fighjendu una altra struttura, chì si trova in a memoria spartuta di l'istanza è hè chjamata ProcArray. Contene una lista di tutti i prucessi attivi, è per ognunu u numeru di a so transazzione attuale (attiva) hè indicatu.
  2. S'ellu hè cumpletu, allora cumu - cumminendu o annullà? Se annullata, allora a versione di fila ùn deve esse micca visibile.
    Questu hè esattamente ciò chì XACT hè per. Ma, ancu s'è l'ultime pagine di XACT sò guardate in buffers in RAM, hè sempre caru per verificà XACT ogni volta. Dunque, una volta chì u statutu di a transazzione hè determinatu, hè scrittu à i bits xmin_committed è xmin_aborted di a versione di stringa. Se unu di sti bits hè stabilitu, allora u statu di transazzione xmin hè cunsideratu cunnisciutu è a transazzione successiva ùn deve micca accede à XACT.

Perchè ùn sò micca sti bits stabiliti da a transazzione stessu chì face l'inserzione? Quandu si faci un inserimentu, a transazzione ùn sapi ancu s'ellu hà da successu. È à u mumentu di l'impegnu, ùn hè più chjaru chì linee in quale pagine sò state cambiate. Ci ponu esse assai di tali pagine, è a memorizazione ùn hè micca prufittu. Inoltre, alcune pagine ponu esse scacciate da u buffer cache à u discu; lighjenduli di novu per cambià i bits rallentaria significativamente l'impegnu.

U svantaghju di u risparmiu hè chì dopu i cambiamenti, ogni transazzione (ancu unu chì faci una lettura simplice - SELECT) pò cumincià à cambià e pagine di dati in u buffer cache.

Allora, correggemu u cambiamentu.

=> COMMIT;

Nunda hè cambiatu in a pagina (ma sapemu chì u statutu di a transazzione hè digià registratu in XACT):

=> SELECT * FROM heap_page('t',0);
 ctid  | state  | xmin | xmax  | t_ctid 
-------+--------+------+-------+--------
 (0,1) | normal | 3664 | 0 (a) | (0,1)
(1 row)

Avà a transazzione chì accede à a pagina prima duverà determinà u statutu di transazzione xmin è scrivite à i bits d'infurmazioni:

=> SELECT * FROM t;
 id |  s  
----+-----
  1 | FOO
(1 row)

=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   | xmax  | t_ctid 
-------+--------+----------+-------+--------
 (0,1) | normal | 3664 (c) | 0 (a) | (0,1)
(1 row)

Delete

Quandu una fila hè sguassata, u numeru di a transazzione di eliminazione attuale hè scrittu à u campu xmax di a versione attuale, è u bit xmax_aborted hè sguassatu.

Nota chì u valore stabilitu di xmax chì currisponde à a transazzione attiva agisce cum'è una serratura di fila. Se una altra transazzione vole aghjurnà o sguassate sta fila, serà furzata à aspittà chì a transazzione xmax finisci. Parleremu di più nantu à u bloccu dopu. Per avà, avemu solu nutà chì u numeru di chjusi di fila hè illimitatu. Ùn occupanu micca spaziu in RAM è u rendiment di u sistema ùn soffre micca u so numeru. True, e transazzione "longa" anu altre disadvantages, ma più nantu à questu dopu.

Sguassemu a linea.

=> BEGIN;
=> DELETE FROM t;
=> SELECT txid_current();
 txid_current 
--------------
         3665
(1 row)

Avemu vistu chì u numeru di transazzione hè scrittu in u campu xmax, ma i bits d'infurmazioni ùn sò micca stati:

=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   | xmax | t_ctid 
-------+--------+----------+------+--------
 (0,1) | normal | 3664 (c) | 3665 | (0,1)
(1 row)

annullamentu

L'annullamentu di i cambiamenti funziona in modu simile à l'impegnu, solu in XACT u bit abortu hè stabilitu per a transazzione. L'annullamentu hè veloce quant'è l'impegnu. Ancu s'ellu u cumandamentu hè chjamatu ROLLBACK, i cambiamenti ùn sò micca ritruvati: tuttu ciò chì a transazzione hà sappiutu cambià in e pagine di dati resta invariatu.

=> ROLLBACK;
=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   | xmax | t_ctid 
-------+--------+----------+------+--------
 (0,1) | normal | 3664 (c) | 3665 | (0,1)
(1 row)

Quandu si accede à a pagina, u statutu serà verificatu è u bit di suggerimentu xmax_aborted serà stabilitu à a versione di fila. U numeru xmax stessu ferma nantu à a pagina, ma nimu l'hà da fighjà.

=> SELECT * FROM t;
 id |  s  
----+-----
  1 | FOO
(1 row)

=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   |   xmax   | t_ctid 
-------+--------+----------+----------+--------
 (0,1) | normal | 3664 (c) | 3665 (a) | (0,1)
(1 row)

Update

L'aghjurnamentu funziona cum'è s'ellu hà prima sguassatu a versione attuale di a fila è dopu inseritu una nova.

=> BEGIN;
=> UPDATE t SET s = 'BAR';
=> SELECT txid_current();
 txid_current 
--------------
         3666
(1 row)

A dumanda produce una linea (nova versione):

=> SELECT * FROM t;
 id |  s  
----+-----
  1 | BAR
(1 row)

Ma nantu à a pagina vedemu e duie versioni:

=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   | xmax  | t_ctid 
-------+--------+----------+-------+--------
 (0,1) | normal | 3664 (c) | 3666  | (0,2)
 (0,2) | normal | 3666     | 0 (a) | (0,2)
(2 rows)

A versione sguassata hè marcata cù u numeru di transazzione attuale in u campu xmax. Inoltre, stu valore hè scrittu annantu à u vechju, postu chì a transazzione precedente hè stata annullata. È u bit xmax_aborted hè sbulicatu perchè u statutu di a transazzione attuale ùn hè micca cunnisciutu.

A prima versione di a linea si riferisce avà à a seconda (campu t_ctid) cum'è a più nova.

Un secondu indice appare in a pagina di l'indici è una seconda fila riferisce a seconda versione in a pagina di a tabella.

Cum'è cù l'eliminazione, u valore xmax in a prima versione di a fila hè un indicazione chì a fila hè chjusa.

Ebbè, compiemu a transazzione.

=> COMMIT;

Indici

Finu à avà avemu parlatu solu di e pagine di tavule. Chì succede in l'indici?

L'infurmazioni in e pagine d'indici varieghja assai sicondu u tipu specificu di l'indici. E ancu un tipu d'indici hà diversi tipi di pagine. Per esempiu, un B-tree hà una pagina di metadata è pagine "regular".

In ogni casu, a pagina di solitu hà un array di puntatori à e fila è e fila stessi (cum'è una pagina di tabella). Inoltre, à a fine di a pagina ci hè spaziu per dati spiciali.

E fila in l'indici pò ancu avè strutture assai diverse secondu u tipu d'indici. Per esempiu, per un B-tree, e fila ligati à e pagine di foglia cuntenenu u valore di chjave d'indexazione è una riferenza (ctid) à a fila di tabella currispondente. In generale, l'indici pò esse strutturatu in una manera completamente diversa.

U puntu più impurtante hè chì ùn ci sò micca versioni di fila in indici di ogni tipu. Ebbè, o pudemu assume chì ogni linea hè rapprisintata da esattamente una versione. In altri palori, ùn ci sò micca campi xmin è xmax in l'intestazione di a fila d'indici. Pudemu assume chì i ligami da l'indici portanu à tutte e versioni di a tavula di e fila - cusì pudete capisce quale versione a transazzione vede solu fighjendu a tavula. (Com'è sempre, questu ùn hè micca tutta a verità. In certi casi, a mappa di visibilità pò ottimisà u prucessu, ma vedemu questu in più detail dopu.)

À u listessu tempu, in a pagina di l'indici truvamu puntatori à e duie versioni, sia l'attuale sia l'antica:

=> SELECT itemoffset, ctid FROM bt_page_items('t_s_idx',1);
 itemoffset | ctid  
------------+-------
          1 | (0,2)
          2 | (0,1)
(2 rows)

Transazzione virtuale

In pratica, PostgreSQL usa ottimisazioni chì permettenu di "salvà" numeri di transazzione.

Se una transazzione leghje solu dati, ùn hà micca effettu nantu à a visibilità di e versioni di fila. Per quessa, u prucessu di serviziu prima emette un xid virtuale à a transazzione. U numeru hè custituitu da un ID di prucessu è un numeru di sequenza.

L'emissione di stu numeru ùn hà micca bisognu di sincronizazione trà tutti i prucessi è hè dunque assai veloce. Avemu da cunnosce un altru mutivu per aduprà numeri virtuali quandu parlemu di congelazione.

I numeri virtuali ùn sò micca cunsiderati in ogni modu in i snapshots di dati.

In parechji punti in u tempu, pò esse bè transazzione virtuale in u sistema cù numeri chì sò digià utilizati, è questu hè normale. Ma un tali numeru ùn pò esse scrittu in e pagine di dati, perchè a prossima volta chì a pagina hè accessu pò perde tuttu u significatu.

=> BEGIN;
=> SELECT txid_current_if_assigned();
 txid_current_if_assigned 
--------------------------
                         
(1 row)

Se una transazzione cumencia à cambià a dati, hè datu un veru numeru di transazzione unicu.

=> UPDATE accounts SET amount = amount - 1.00;
=> SELECT txid_current_if_assigned();
 txid_current_if_assigned 
--------------------------
                     3667
(1 row)

=> COMMIT;

Transazzioni nidificate

Salvà i punti

Definitu in SQL salvà punti (puntu di salvezza), chì permettenu di annullà una parte di una transazzione senza interrompe completamente. Ma questu ùn si mette micca in u diagramma di sopra, postu chì a transazzione hà u stessu statutu per tutti i so cambiamenti, è fisicamenti nisuna data hè ritruvata.

Per implementà sta funziunalità, una transazzione cù un puntu di salvezza hè divisu in parechji separati transazzione nidificatu (subtransaction), chì u statutu pò esse amministratu separatamente.

I transacciones nidificate anu u so propiu numeru (più altu ch'è u numeru di a transazzione principale). U statutu di transazzione nidificatu hè arregistratu in u modu abituale in XACT, ma u statutu finali dipende da u statutu di a transazzione principale: se hè annullata, tutte e transazzione anidata sò ancu annullate.

L'infurmazione nantu à a nidificazione di transazzione hè almacenata in i schedari in u cartulare PGDATA/pg_subtrans. I schedari sò accede à traversu buffers in a memoria sparta di l'istanza, urganizata in u listessu modu cum'è i buffer XACT.

Ùn cunfundite transazzione nidificatu cù transazzioni autonomi. E transazzione autònuma ùn dependenu micca di l'altri in ogni modu, ma e transazzione nidificate. Ùn ci hè micca transazzione autonoma in PostgreSQL regulare, è, forsi, per u megliu: sò necessarii assai, assai raramenti, è a so prisenza in altri DBMS pruvucanu l'abusu, da quale tutti soffrenu.

Limpiemu a tavula, cuminciamu una transazzione è inserisci a fila:

=> TRUNCATE TABLE t;
=> BEGIN;
=> INSERT INTO t(s) VALUES ('FOO');
=> SELECT txid_current();
 txid_current 
--------------
         3669
(1 row)

=> SELECT xmin, xmax, * FROM t;
 xmin | xmax | id |  s  
------+------+----+-----
 3669 |    0 |  2 | FOO
(1 row)

=> SELECT * FROM heap_page('t',0);
 ctid  | state  | xmin | xmax  | t_ctid 
-------+--------+------+-------+--------
 (0,1) | normal | 3669 | 0 (a) | (0,1)
(1 row)

Avà mette un puntu di salvezza è inserisce una altra linea.

=> SAVEPOINT sp;
=> INSERT INTO t(s) VALUES ('XYZ');
=> SELECT txid_current();
 txid_current 
--------------
         3669
(1 row)

Nota chì a funzione txid_current() torna u numeru di transazzione principale, micca u numeru di transazzione nidificatu.

=> SELECT xmin, xmax, * FROM t;
 xmin | xmax | id |  s  
------+------+----+-----
 3669 |    0 |  2 | FOO
 3670 |    0 |  3 | XYZ
(2 rows)

=> SELECT * FROM heap_page('t',0);
 ctid  | state  | xmin | xmax  | t_ctid 
-------+--------+------+-------+--------
 (0,1) | normal | 3669 | 0 (a) | (0,1)
 (0,2) | normal | 3670 | 0 (a) | (0,2)
(2 rows)

Riturnemu à u puntu di salvezza è inserite a terza linea.

=> ROLLBACK TO sp;
=> INSERT INTO t(s) VALUES ('BAR');
=> SELECT xmin, xmax, * FROM t;
 xmin | xmax | id |  s  
------+------+----+-----
 3669 |    0 |  2 | FOO
 3671 |    0 |  4 | BAR
(2 rows)

=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   | xmax  | t_ctid 
-------+--------+----------+-------+--------
 (0,1) | normal | 3669     | 0 (a) | (0,1)
 (0,2) | normal | 3670 (a) | 0 (a) | (0,2)
 (0,3) | normal | 3671     | 0 (a) | (0,3)
(3 rows)

In a pagina, cuntinuemu à vede a fila aghjuntu da a transazzione anidata annullata.

Fixemu i cambiamenti.

=> COMMIT;
=> SELECT xmin, xmax, * FROM t;
 xmin | xmax | id |  s  
------+------+----+-----
 3669 |    0 |  2 | FOO
 3671 |    0 |  4 | BAR
(2 rows)

=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   | xmax  | t_ctid 
-------+--------+----------+-------+--------
 (0,1) | normal | 3669 (c) | 0 (a) | (0,1)
 (0,2) | normal | 3670 (a) | 0 (a) | (0,2)
 (0,3) | normal | 3671 (c) | 0 (a) | (0,3)
(3 rows)

Avà pudete vede chjaramente chì ogni transazzione anidata hà u so propiu statutu.

Nota chì e transazzione nidificate ùn ponu micca esse aduprate esplicitamente in SQL, vale à dì, ùn pudete micca inizià una nova transazzione senza cumpiendu l'attuale. Stu mekanismu hè attivatu implicitamente quandu si usanu punti di salvezza, è ancu quandu si tratta l'eccezzioni PL / pgSQL è in una quantità di altri casi più esotici.

=> BEGIN;
BEGIN
=> BEGIN;
WARNING:  there is already a transaction in progress
BEGIN
=> COMMIT;
COMMIT
=> COMMIT;
WARNING:  there is no transaction in progress
COMMIT

Errori è atomicità di l'operazioni

Chì succede se un errore si faci durante l'operazione? Per esempiu, cusì:

=> BEGIN;
=> SELECT * FROM t;
 id |  s  
----+-----
  2 | FOO
  4 | BAR
(2 rows)

=> UPDATE t SET s = repeat('X', 1/(id-4));
ERROR:  division by zero

Un errore hè accadutu. Avà a transazzione hè cunsiderata abortita è nisuna operazione hè permessa in questu:

=> SELECT * FROM t;
ERROR:  current transaction is aborted, commands ignored until end of transaction block

E ancu s'è pruvate à cummettà i cambiamenti, PostgreSQL hà da signalà un abortu:

=> COMMIT;
ROLLBACK

Perchè una transazzione ùn pò micca cuntinuà dopu un fallimentu? U fattu hè chì un errore puderia esse in una tale manera chì averemu accessu à una parte di i cambiamenti - l'atomicità di micca ancu a transazzione, ma l'operatore seria violatu. Cum'è in u nostru esempiu, induve l'operatore hà sappiutu aghjurnà una linea prima di l'errore:

=> SELECT * FROM heap_page('t',0);
 ctid  | state  |   xmin   | xmax  | t_ctid 
-------+--------+----------+-------+--------
 (0,1) | normal | 3669 (c) | 3672  | (0,4)
 (0,2) | normal | 3670 (a) | 0 (a) | (0,2)
 (0,3) | normal | 3671 (c) | 0 (a) | (0,3)
 (0,4) | normal | 3672     | 0 (a) | (0,4)
(4 rows)

Ci vole à dì chì psql hà un modu chì permette ancu a transazzione di cuntinuà dopu à un fallimentu cum'è se l'azzioni di l'operatore erronee sò stati rimossi.

=> set ON_ERROR_ROLLBACK on
=> BEGIN;
=> SELECT * FROM t;
 id |  s  
----+-----
  2 | FOO
  4 | BAR
(2 rows)

=> UPDATE t SET s = repeat('X', 1/(id-4));
ERROR:  division by zero

=> SELECT * FROM t;
 id |  s  
----+-----
  2 | FOO
  4 | BAR
(2 rows)

=> COMMIT;

Ùn hè micca difficiule di guessà chì in questu modu, psql mette in realtà un puntu di salvezza implicita prima di ogni cumanda, è in casu di fallimentu inizia un rollback à questu. Stu modu ùn hè micca utilizatu per difettu, postu chì l'impostazione di punti di salvezza (ancu senza vultà à elli) implica un overhead significativu.

Continuazione.

Source: www.habr.com

Add a comment