MVCC-3. Strengjaútgáfur

Svo höfum við íhugað mál sem tengjast einangrun, og gerði hörfa um skipuleggja gögn á lágu stigi. Og að lokum komum við að áhugaverðasta hlutanum - strengjaútgáfunum.

Titill

Eins og við höfum þegar sagt, getur hver röð verið til í nokkrum útgáfum í gagnagrunninum samtímis. Ein útgáfa verður að vera einhvern veginn aðgreind frá annarri. Í þessu skyni hefur hver útgáfa tvö merki sem ákvarða „tíma“ verkunar þessarar útgáfu (xmin og xmax). Innan gæsalappa - því það er ekki tíminn sem slíkur sem er notaður heldur sérstakur hækkandi teljari. Og þessi teljari er viðskiptanúmerið.

(Eins og venjulega er raunveruleikinn flóknari: færslunúmerið getur ekki aukist stöðugt vegna takmarkaðrar bitagetu teljarans. En við munum skoða þessar upplýsingar í smáatriðum þegar við komum að frystingu.)

Þegar röð er búin til er xmin stillt á færslunúmerið sem gaf út INSERT skipunina og xmax er skilið eftir autt.

Þegar röð er eytt er xmax gildi núverandi útgáfu merkt með númeri færslunnar sem framkvæmdi DELETE.

Þegar röð er breytt með UPDATE skipun eru tvær aðgerðir í raun gerðar: DELETE og INSERT. Núverandi útgáfa af línunni setur xmax jafnt og númer færslunnar sem framkvæmdi UPPfærsluna. Ný útgáfa af sama streng er síðan búin til; xmin gildi þess fellur saman við xmax gildi fyrri útgáfu.

Xmin og xmax reitirnir eru innifalin í línuútgáfuhausnum. Auk þessara reita inniheldur hausinn önnur, til dæmis:

  • infomask er röð bita sem skilgreina eiginleika þessarar útgáfu. Þeir eru talsvert margir; Við munum smám saman íhuga þær helstu.
  • ctid er tengill á næstu, nýrri útgáfu af sömu línu. Fyrir nýjustu, nýjustu útgáfuna af línu vísar ctid til þessarar útgáfu sjálfrar. Talan hefur formið (x,y), þar sem x er blaðsíðutal, y er vísitala í fylkinu.
  • null bitmap - Merkir þá dálka í tiltekinni útgáfu sem innihalda núllgildi (NULL). NULL er ekki eitt af venjulegum gagnategundargildum, þannig að eigindin verður að vera geymd sérstaklega.

Fyrir vikið er hausinn nokkuð stór - að minnsta kosti 23 bæti fyrir hverja útgáfu af línunni, og venjulega meira vegna NULL punktamyndarinnar. Ef taflan er "þröng" (þ.e. inniheldur fáa dálka) gæti kostnaðurinn tekið meira en gagnlegar upplýsingar.

setja

Við skulum skoða nánar hvernig strengjaaðgerðir á lágu stigi eru framkvæmdar, byrja á innsetningu.

Fyrir tilraunir skulum við búa til nýja töflu með tveimur dálkum og vísitölu á einum þeirra:

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

Setjum inn eina línu eftir að viðskipti eru hafin.

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

Hér er núverandi viðskiptanúmer okkar:

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

Við skulum skoða innihald síðunnar. Heap_page_items aðgerðin í pageinspect viðbótinni gerir þér kleift að fá upplýsingar um ábendingar og línuútgáfur:

=> 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

Athugaðu að orðið hrúga í PostgreSQL vísar til töflur. Þetta er önnur undarleg notkun hugtaksins - hrúga er þekkt gagnagrind, sem á ekkert sameiginlegt með töflunni. Hér er orðið notað í merkingunni „allt er hent saman,“ öfugt við skipaðar vísitölur.

Aðgerðin sýnir gögn „eins og þau eru“ á sniði sem er erfitt að skilja. Til að komast að því munum við skilja aðeins eftir hluta upplýsinganna og ráða þær:

=> 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)

Hér er það sem við gerðum:

  • Bætti núlli við vísitöluna til að hún líti eins út og t_ctid: (síðunúmer, vísitölu).
  • Túlkaði stöðu lp_flags bendillsins. Hér er það "eðlilegt" - þetta þýðir að bendillinn vísar í raun til útgáfu strengsins. Við skoðum aðrar merkingar síðar.
  • Af öllum upplýsingabitum hafa aðeins tvö pör verið auðkennd hingað til. Bitarnir xmin_committed og xmin_aborted gefa til kynna hvort færslunúmer xmin sé framkvæmt (hætt við). Tveir svipaðir bitar vísa til færslunúmers xmax.

Hvað sjáum við? Þegar röð er sett inn birtist vísitala 1 á töflusíðunni sem bendir á fyrstu og einu útgáfuna af línunni.

Í strengjaútgáfunni er xmin reiturinn fylltur með núverandi færslunúmeri. Færslan er enn virk, þannig að bæði xmin_committed og xmin_aborted bitarnir eru ekki stilltir.

Röð útgáfa ctid reiturinn vísar til sömu línu. Þetta þýðir að nýrri útgáfa er ekki til.

Xmax reiturinn er fylltur með dummy númeri 0 vegna þess að þessari útgáfu af línunni hefur ekki verið eytt og er núverandi. Viðskipti munu ekki taka eftir þessari tölu vegna þess að xmax_aborted bitinn er stilltur.

Tökum enn eitt skrefið í átt að því að bæta læsileika með því að bæta upplýsingabitum við færslunúmer. Og við skulum búa til aðgerð, þar sem við þurfum beiðnina oftar en einu sinni:

=> 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;

Í þessu formi er miklu skýrara hvað er að gerast í hausnum á línuútgáfunni:

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

Svipaðar, en verulega minna ítarlegar, upplýsingar er hægt að fá úr töflunni sjálfri með því að nota gervisálka xmin og xmax:

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

Festa

Ef færslu er lokið þarftu að muna stöðu hennar - athugaðu að hún er skuldbundin. Til þess er uppbygging sem kallast XACT notað (og fyrir útgáfu 10 var það kallað CLOG (commit log) og þetta nafn er enn að finna á mismunandi stöðum).

XACT er ekki kerfislistatafla; þetta eru skrárnar í PGDATA/pg_xact möppunni. Þeim er úthlutað tveimur bitum fyrir hverja færslu: framið og aflýst - alveg eins og í hausnum í röð útgáfunnar. Þessum upplýsingum er skipt í nokkrar skrár eingöngu til þæginda; við munum snúa aftur að þessu máli þegar við íhugum frystingu. Og vinna með þessar skrár fer fram síðu fyrir síðu, eins og með allar aðrar.

Svo, þegar viðskipti eru framin í XACT, er skuldbundinn bitinn stilltur fyrir þessa færslu. Og þetta er allt sem gerist við framsetningu (þó við séum ekki að tala um forupptökuskrána ennþá).

Þegar önnur færsla fer inn á töflusíðuna sem við skoðuðum nýlega verður hún að svara nokkrum spurningum.

  1. Er xmin færslunni lokið? Ef ekki, þá ætti stofnuð útgáfa af strengnum ekki að vera sýnileg.
    Þessi athugun er framkvæmd með því að skoða aðra uppbyggingu, sem er staðsett í sameiginlegu minni tilviksins og kallast ProcArray. Það inniheldur lista yfir alla virka ferla og fyrir hvern og einn er númer núverandi (virkra) viðskipta þess tilgreint.
  2. Ef því er lokið, hvernig - með því að skuldbinda sig eða hætta við? Ef hætt er við þá ætti línuútgáfan ekki að vera sýnileg heldur.
    Þetta er einmitt það sem XACT er fyrir. En þó að síðustu síður XACT séu geymdar í biðminni í vinnsluminni, þá er samt dýrt að athuga XACT í hvert skipti. Þess vegna, þegar færslustaðan er ákvörðuð, er hún skrifuð á xmin_committed og xmin_aborted bita strengjaútgáfunnar. Ef einn af þessum bitum er stilltur, þá er ástand viðskipta xmin talið þekkt og næsta færsla mun ekki þurfa að fá aðgang að XACT.

Af hverju eru þessir bitar ekki stilltir af færslunni sjálfri sem gerir innskotið? Þegar innsetning á sér stað veit viðskiptin ekki enn hvort hún muni takast. Og á því augnabliki sem skuldbindingin er gerð er ekki lengur ljóst hvaða línum var breytt á hvaða síðum. Það getur verið mikið af slíkum síðum og það er óarðbært að leggja þær á minnið. Að auki er hægt að henda sumum síðum úr biðminni skyndiminni yfir á disk; að lesa þær aftur til að breyta bitunum myndi hægja verulega á skuldbindingunni.

Gallinn við sparnaðinn er sá að eftir breytingar geta allar færslur (jafnvel þeir sem framkvæma einfalda lestur - SELECT) byrjað að breyta gagnasíðum í biðminni skyndiminni.

Svo, við skulum laga breytinguna.

=> COMMIT;

Ekkert hefur breyst á síðunni (en við vitum að viðskiptastaðan er þegar skráð í XACT):

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

Nú verður færslan sem fer fyrst inn á síðuna að ákvarða xmin færslustöðuna og skrifa hana í upplýsingabitana:

=> 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)

Eyða

Þegar röð er eytt er númer núverandi eyðingarfærslu skrifað í xmax reitinn í núverandi útgáfu og xmax_aborted bitinn hreinsaður.

Athugaðu að stillt gildi xmax sem samsvarar virku færslunni virkar sem línulás. Ef önnur færsla vill uppfæra eða eyða þessari línu neyðist hún til að bíða eftir að færslu xmax sé lokið. Við munum tala meira um lokun síðar. Í bili tökum við bara eftir því að fjöldi línulása er ótakmarkaður. Þeir taka ekki upp pláss í vinnsluminni og afköst kerfisins líða ekki fyrir fjölda þeirra. Að vísu hafa „löng“ viðskipti aðra ókosti, en meira um það síðar.

Eyðum línunni út.

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

Við sjáum að færslunúmerið er skrifað í xmax reitinn, en upplýsingabitarnir eru ekki stilltir:

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

Afpöntun

Að hætta við breytingar virkar á svipaðan hátt og að skuldbinda sig, aðeins í XACT er aflýsti bitinn stilltur fyrir færsluna. Að afturkalla er eins fljótt og að skuldbinda sig. Þó skipunin sé kölluð ROLLBACK, eru breytingar ekki afturkallaðar: allt sem færslunni tókst að breyta á gagnasíðunum helst óbreytt.

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

Þegar síðan er opnuð verður staðan skoðuð og xmax_aborted hint bitinn verður stilltur á línuútgáfuna. Xmax talan sjálf er áfram á síðunni, en enginn mun skoða hana.

=> 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)

Uppfæra

Uppfærslan virkar eins og hún hafi fyrst eytt núverandi útgáfu af línunni og síðan sett inn ný.

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

Fyrirspurnin framleiðir eina línu (ný útgáfa):

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

En á síðunni sjáum við báðar útgáfurnar:

=> 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)

Eydd útgáfa er merkt með núverandi færslunúmeri í xmax reitnum. Þar að auki er þetta gildi skrifað yfir það gamla, þar sem fyrri færslu var hætt. Og xmax_aborted bitinn er hreinsaður vegna þess að staða núverandi viðskipta er ekki enn þekkt.

Fyrsta útgáfan af línunni vísar nú til hinnar (t_ctid reitsins) sem nýrri.

Önnur vísir birtist á vísitölusíðunni og önnur röð vísar til annarrar útgáfunnar á töflusíðunni.

Rétt eins og með eyðingu er xmax gildið í fyrstu útgáfu línunnar vísbending um að röðin sé læst.

Jæja, við skulum klára viðskiptin.

=> COMMIT;

Vísitölur

Hingað til höfum við aðeins talað um töflusíður. Hvað gerist inni í vísitölunum?

Upplýsingarnar á vísitölusíðum eru mjög mismunandi eftir tiltekinni gerð vísitölu. Og jafnvel ein tegund vísitölu hefur mismunandi gerðir af síðum. Til dæmis hefur B-tré lýsigagnasíðu og „venjulegar“ síður.

Hins vegar er síðan venjulega með fjölda vísbendinga á línurnar og línurnar sjálfar (alveg eins og töflusíða). Að auki er aftast á síðunni pláss fyrir sérstök gögn.

Raðir í vísitölum geta líka haft mjög mismunandi uppbyggingu eftir tegund vísitölu. Til dæmis, fyrir B-tré, innihalda línurnar sem tengjast blaðsíðum vísitölulykillinn og tilvísun (ctid) í samsvarandi töflulínu. Almennt séð er hægt að byggja upp vísitöluna á allt annan hátt.

Mikilvægasti punkturinn er að það eru engar línuútgáfur í vísitölum af hvaða gerð sem er. Jæja, eða við getum gert ráð fyrir að hver lína sé táknuð með nákvæmlega einni útgáfu. Með öðrum orðum, það eru engir xmin og xmax reitir í haus vísitölunnar. Við getum gert ráð fyrir að tenglar úr vísitölunni leiði til allra töfluútgáfu af línunum - þannig að þú getur fundið út hvaða útgáfu viðskiptin munu sjá aðeins með því að skoða töfluna. (Eins og alltaf er þetta ekki allur sannleikurinn. Í sumum tilfellum getur sýnileikakortið fínstillt ferlið, en við munum skoða þetta nánar síðar.)

Á sama tíma finnum við á vísitölusíðunni vísbendingar um báðar útgáfurnar, bæði núverandi og gömlu:

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

Sýndarviðskipti

Í reynd notar PostgreSQL hagræðingar sem gera því kleift að „vista“ viðskiptanúmer.

Ef færsla les aðeins gögn hefur það engin áhrif á sýnileika línuútgáfu. Þess vegna gefur þjónustuferlið fyrst út sýndar-xid fyrir viðskiptin. Númerið samanstendur af ferli ID og raðnúmeri.

Útgáfa þessa númers krefst ekki samstillingar á milli allra ferla og er því mjög hröð. Við munum kynnast annarri ástæðu fyrir notkun sýndarnúmera þegar við tölum um frystingu.

Sýndarnúmer eru ekki tekin með í reikninginn á nokkurn hátt í skyndimyndum gagna.

Á mismunandi tímapunktum geta vel verið sýndarfærslur í kerfinu með númer sem þegar hafa verið notuð og það er eðlilegt. En slíkt númer er ekki hægt að skrifa inn á gagnasíður, því næst þegar síðan er opnuð gæti hún misst alla merkingu.

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

Ef færslu byrjar að breyta gögnum er henni gefið raunverulegt, einstakt færslunúmer.

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

=> COMMIT;

Hreiður viðskipti

Sparaðu stig

Skilgreint í SQL spara stig (savepoint), sem gerir þér kleift að hætta við hluta viðskipta án þess að trufla hana alveg. En þetta passar ekki inn í skýringarmyndina hér að ofan, þar sem viðskiptin hafa sömu stöðu fyrir allar breytingar og líkamlega er engin gögn afturkölluð.

Til að útfæra þessa virkni er færslu með vistunarpunkti skipt í nokkra aðskilda hreiður viðskipti (undirfærslu), sem hægt er að stjórna sérstaklega.

Hreiður færslur hafa sitt eigið númer (hærra en númer aðalfærslunnar). Staða hreiðraðra færslna er skráð á venjulegan hátt í XACT, en endanleg staða fer eftir stöðu aðalfærslunnar: ef hún er hætt, þá falla einnig allar hreiðraðar færslur niður.

Upplýsingar um hreiðurfærslur eru geymdar í skrám í PGDATA/pg_subtrans skránni. Aðgangur er að skrám í gegnum biðminni í sameiginlegu minni tilviksins, skipulögð á sama hátt og XACT biðminni.

Ekki rugla saman hreiðrum viðskiptum og sjálfstæðum viðskiptum. Sjálfstæð viðskipti eru ekki háð hvort öðru á nokkurn hátt, en hreiður viðskipti gera það. Það eru engar sjálfstæðar færslur í venjulegum PostgreSQL, og ef til vill fyrir það besta: þeirra er þörf mjög, mjög sjaldan, og nærvera þeirra í öðrum DBMS veldur misnotkun, sem allir þjást af.

Við skulum hreinsa töfluna, hefja færslu og setja inn línuna:

=> 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)

Nú skulum við setja vistunarpunkt og setja inn aðra línu.

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

Athugaðu að txid_current() fallið skilar aðalfærslunúmerinu, ekki hreidda færslunúmerinu.

=> 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)

Snúum okkur aftur að vistunarpunktinum og setjum inn þriðju línuna.

=> 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)

Á síðunni höldum við áfram að sjá línuna sem hætt er við hreiður færslu.

Við lagfærum breytingarnar.

=> 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)

Nú geturðu greinilega séð að hver hreiðruð færsla hefur sína eigin stöðu.

Athugaðu að ekki er hægt að nota hreiður færslur beinlínis í SQL, það er, þú getur ekki hafið nýja færslu án þess að klára núverandi. Þetta fyrirkomulag er virkjað óbeint þegar vistunarpunktar eru notaðir, sem og við meðhöndlun PL/pgSQL undantekningar og í fjölda annarra framandi tilvika.

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

Villur og atómvirkni aðgerða

Hvað gerist ef villa kemur upp þegar aðgerð er framkvæmd? Til dæmis, svona:

=> 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

Villa hefur komið upp. Nú eru viðskiptin talin hætt og engar aðgerðir eru leyfðar í þeim:

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

Og jafnvel þótt þú reynir að framfylgja breytingunum mun PostgreSQL tilkynna um stöðvun:

=> COMMIT;
ROLLBACK

Af hverju geta viðskipti ekki haldið áfram eftir bilun? Staðreyndin er sú að mistök gætu komið upp á þann hátt að við myndum fá aðgang að hluta breytinganna - atomic ekki einu sinni viðskiptin, heldur væri brotið á rekstraraðilanum. Eins og í dæminu okkar, þar sem rekstraraðilanum tókst að uppfæra eina línu fyrir villuna:

=> 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)

Það verður að segjast að psql er með stillingu sem gerir enn kleift að halda viðskiptunum áfram eftir bilun eins og aðgerðir rangra rekstraraðila væru afturkallaðar.

=> 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;

Það er ekki erfitt að giska á að í þessum ham setur psql í raun óbeinan vistunarpunkt fyrir hverja skipun og ef bilun kemur af stað afturköllun á hana. Þessi háttur er ekki notaður sjálfgefið, þar sem að setja vistunarpunkta (jafnvel án þess að snúa aftur til þeirra) felur í sér verulegt kostnaðarauka.

Framhald.

Heimild: www.habr.com

Bæta við athugasemd