MVCC-3. Matoleo ya kamba

Kwa hiyo, tumezingatia masuala yanayohusiana na insulation, na akafanya mafungo kuhusu kupanga data kwa kiwango cha chini. Na hatimaye tulifika sehemu ya kuvutia zaidi - matoleo ya kamba.

Cheo

Kama tulivyokwisha sema, kila safu inaweza kuwepo kwa wakati mmoja katika matoleo kadhaa kwenye hifadhidata. Toleo moja lazima litofautishwe kwa njia fulani na lingine. Kwa kusudi hili, kila toleo lina alama mbili zinazobainisha "wakati" wa kitendo cha toleo hili (xmin na xmax). Katika nukuu - kwa sababu sio wakati kama huo ambao hutumiwa, lakini kihesabu maalum cha kuongezeka. Na kaunta hii ndio nambari ya muamala.

(Kama kawaida, hali halisi ni ngumu zaidi: nambari ya muamala haiwezi kuongezeka kila wakati kwa sababu ya uwezo mdogo wa kaunta. Lakini tutaangalia maelezo haya kwa undani tunapofikia kufungia.)

Safu mlalo inapoundwa, xmin imewekwa kwa nambari ya muamala iliyotoa amri ya INSERT, na xmax inaachwa wazi.

Safu mlalo inapofutwa, thamani ya xmax ya toleo la sasa inatiwa alama na nambari ya muamala uliofanya DELETE.

Wakati safu mlalo inarekebishwa na amri ya UPDATE, shughuli mbili zinafanywa: DELETE na INSERT. Toleo la sasa la safu mlalo huweka xmax sawa na idadi ya shughuli iliyofanya USASISHAJI. Toleo jipya la mfuatano huo huo linaundwa; thamani yake ya xmin inalingana na thamani ya xmax ya toleo la awali.

Sehemu za xmin na xmax zimejumuishwa kwenye kichwa cha toleo la safu mlalo. Mbali na sehemu hizi, kichwa kina zingine, kwa mfano:

  • infomask ni mfululizo wa biti zinazofafanua sifa za toleo hili. Kuna mengi yao; Tutazingatia hatua kwa hatua zile kuu.
  • ctid ni kiungo cha toleo linalofuata, jipya zaidi la mstari huo huo. Kwa toleo jipya zaidi, la sasa zaidi la mfuatano, ctid inarejelea toleo hili lenyewe. Nambari ina fomu (x, y), ambapo x ni nambari ya ukurasa, y ni nambari ya faharasa katika safu.
  • null bitmap - Huweka alama kwenye safu wima hizo za toleo fulani ambalo lina thamani isiyofaa (NULL). NULL si mojawapo ya thamani za aina ya data ya kawaida, kwa hivyo sifa lazima ihifadhiwe kando.

Matokeo yake, kichwa ni kikubwa kabisa - angalau ka 23 kwa kila toleo la mstari, na kwa kawaida zaidi kutokana na bitmap NULL. Ikiwa jedwali ni "nyembamba" (hiyo ni, ina safu wima chache), kichwa cha juu kinaweza kuchukua zaidi ya habari muhimu.

kuingiza

Hebu tuchunguze kwa undani jinsi shughuli za kamba za kiwango cha chini zinafanywa, kuanzia na kuingizwa.

Kwa majaribio, hebu tuunde jedwali jipya lenye safu wima mbili na faharasa kwenye mojawapo yao:

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

Hebu tuingize safu mlalo moja baada ya kuanza muamala.

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

Hii ndio nambari yetu ya sasa ya muamala:

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

Wacha tuangalie yaliyomo kwenye ukurasa. Kitendaji cha heap_page_items cha kiendelezi cha kukagua ukurasa hukuruhusu kupata maelezo kuhusu viashiria na matoleo ya safu mlalo:

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

Kumbuka kuwa neno lundo katika PostgreSQL linarejelea majedwali. Hii ni matumizi mengine ya ajabu ya neno - lundo inajulikana muundo wa data, ambayo haina uhusiano wowote na meza. Hapa neno hilo linatumiwa katika maana ya β€œkila kitu hutupwa pamoja,” kinyume na fahirisi zilizoamriwa.

Kazi inaonyesha data "kama ilivyo", katika muundo ambao ni vigumu kuelewa. Ili kuibaini, tutaacha sehemu tu ya habari na kuifafanua:

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

Hivi ndivyo tulifanya:

  • Imeongeza sifuri kwa nambari ya faharasa ili kuifanya ionekane sawa na t_ctid: (nambari ya ukurasa, nambari ya faharasa).
  • Imebainisha hali ya kielekezi cha lp_flags. Hapa ni "kawaida" - hii ina maana kwamba pointer kweli inahusu toleo la kamba. Tutaangalia maana zingine baadaye.
  • Kati ya vipande vyote vya habari, ni jozi mbili tu zimetambuliwa hadi sasa. Biti za xmin_committed na xmin_aborted zinaonyesha kama nambari ya muamala xmin imefanywa (imebatilishwa). Biti mbili zinazofanana hurejelea nambari ya muamala xmax.

Tunaona nini? Unapoingiza safu mlalo, nambari ya 1 itaonekana kwenye ukurasa wa jedwali, ikionyesha toleo la kwanza na la pekee la safu mlalo.

Katika toleo la kamba, uga wa xmin umejaa nambari ya sasa ya muamala. Muamala bado unatumika, kwa hivyo sehemu zote mbili za xmin_committed na xmin_aborted hazijawekwa.

Sehemu ya safu mlalo ya ctid inarejelea safu mlalo sawa. Hii ina maana kwamba toleo jipya zaidi halipo.

Sehemu ya xmax imejaa nambari dummy 0 kwa sababu toleo hili la safu mlalo halijafutwa na ni la sasa. Shughuli za malipo hazitazingatia nambari hii kwa sababu sehemu ya xmax_aborted imewekwa.

Hebu tuchukue hatua moja zaidi kuelekea kuboresha usomaji kwa kuongeza vipande vya habari kwenye nambari za muamala. Na wacha tuunda kitendakazi, kwani tutahitaji ombi zaidi ya mara moja:

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

Katika fomu hii, ni wazi zaidi kile kinachoendelea kwenye kichwa cha toleo la safu:

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

Sawa, lakini maelezo kidogo sana, habari inaweza kupatikana kutoka kwa jedwali yenyewe, kwa kutumia safu wima za xmin na xmax:

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

Urekebishaji

Ikiwa shughuli imekamilika kwa ufanisi, unahitaji kukumbuka hali yake - kumbuka kuwa imefanywa. Ili kufanya hivyo, muundo unaoitwa XACT hutumiwa (na kabla ya toleo la 10 uliitwa CLOG (logi ya kujitolea) na jina hili bado linaweza kupatikana katika maeneo tofauti).

XACT sio jedwali la orodha ya mfumo; hizi ni faili kwenye saraka ya PGDATA/pg_xact. Zina biti mbili kwa kila muamala: zimejitolea na kusitishwa - kama tu kwenye kichwa cha toleo la safu mlalo. Habari hii imegawanywa katika faili kadhaa kwa urahisi; tutarudi kwa suala hili tunapozingatia kufungia. Na kufanya kazi na faili hizi hufanywa ukurasa kwa ukurasa, kama na wengine wote.

Kwa hivyo, wakati muamala unafanywa katika XACT, kiasi kilichotolewa kinawekwa kwa muamala huu. Na hii ndiyo yote hutokea wakati wa kufanya (ingawa bado hatuzungumzii kuhusu logi ya kurekodi kabla).

Muamala mwingine unapofikia ukurasa wa jedwali ambao tumeutazama hivi punde, italazimika kujibu maswali kadhaa.

  1. Je, shughuli ya xmin imekamilika? Ikiwa sio, basi toleo lililoundwa la kamba haipaswi kuonekana.
    Cheki hiki kinafanywa kwa kuangalia muundo mwingine, ambao uko kwenye kumbukumbu iliyoshirikiwa ya mfano na inaitwa ProcArray. Ina orodha ya michakato yote inayofanya kazi, na kwa kila moja idadi ya shughuli yake ya sasa (ya kazi) imeonyeshwa.
  2. Ikiwa imekamilika, basi vipi - kwa kufanya au kughairi? Ikiwa imeghairiwa, basi toleo la safu haipaswi kuonekana pia.
    Hii ndio hasa XACT ni ya. Lakini, ingawa kurasa za mwisho za XACT zimehifadhiwa katika vihifadhi kwenye RAM, bado ni ghali kuangalia XACT kila wakati. Kwa hivyo, mara tu hali ya muamala imebainishwa, inaandikwa kwa xmin_committed na xmin_aborted bits ya toleo la kamba. Ikiwa moja ya biti hizi imewekwa, basi hali ya muamala xmin inachukuliwa kujulikana na muamala unaofuata hautalazimika kufikia XACT.

Kwa nini biti hizi hazijawekwa na shughuli yenyewe inayoingiza? Ingizo linapotokea, muamala bado haujui kama utafaulu. Na wakati wa kujitolea, haijulikani tena ni mistari gani ambayo kurasa zilibadilishwa. Kunaweza kuwa na kurasa nyingi kama hizo, na kuzikariri sio faida. Kwa kuongeza, baadhi ya kurasa zinaweza kuondolewa kutoka kwa kashe ya bafa hadi kwenye diski; kuzisoma tena ili kubadilisha bits kunaweza kupunguza kasi ya ahadi kwa kiasi kikubwa.

Upande wa chini wa akiba ni kwamba baada ya mabadiliko, shughuli yoyote (hata moja inayofanya kusoma rahisi - CHAGUA) inaweza kuanza kubadilisha kurasa za data kwenye kashe ya bafa.

Kwa hivyo, wacha turekebishe mabadiliko.

=> COMMIT;

Hakuna kilichobadilika kwenye ukurasa (lakini tunajua kuwa hali ya muamala tayari imerekodiwa katika XACT):

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

Sasa muamala unaofikia ukurasa kwanza italazimika kuamua hali ya muamala ya xmin na kuiandika kwa sehemu za habari:

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

Kuondolewa

Safu mlalo inapofutwa, nambari ya shughuli ya sasa ya kufuta inaandikwa kwa uga wa xmax wa toleo la sasa, na sehemu ya xmax_aborted itafutwa.

Kumbuka kuwa thamani iliyowekwa ya xmax inayolingana na shughuli inayotumika hufanya kama kufuli ya safu mlalo. Ikiwa muamala mwingine unataka kusasisha au kufuta safu mlalo hii, italazimika kusubiri hadi shughuli ya xmax ikamilike. Tutazungumza zaidi juu ya kuzuia baadaye. Kwa sasa, tunaona tu kwamba idadi ya kufuli ya safu haina ukomo. Hawana nafasi katika RAM na utendaji wa mfumo hauteseka na idadi yao. Kweli, shughuli za "muda mrefu" zina hasara nyingine, lakini zaidi juu ya hilo baadaye.

Hebu tufute mstari.

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

Tunaona kwamba nambari ya muamala imeandikwa katika uga wa xmax, lakini biti za habari hazijawekwa:

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

Kufuta

Kukomesha mabadiliko hufanya kazi sawa na kujitolea, katika XACT tu sehemu iliyofutwa imewekwa kwa ajili ya shughuli. Kutendua ni haraka kama kujituma. Ingawa amri inaitwa ROLLBACK, mabadiliko hayarudishwi nyuma: kila kitu ambacho shughuli iliweza kubadilisha katika kurasa za data bado haijabadilika.

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

Ukurasa unapofikiwa, hali itaangaliwa na kidokezo cha xmax_aborted kitawekwa kwenye toleo la safu mlalo. Nambari ya xmax yenyewe inabaki kwenye ukurasa, lakini hakuna mtu atakayeiangalia.

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

Sasisha

Sasisho hufanya kazi kana kwamba ilifuta toleo la sasa la safu mlalo kwanza na kisha kuingiza jipya.

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

Swala hutoa mstari mmoja (toleo jipya):

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

Lakini kwenye ukurasa tunaona matoleo yote mawili:

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

Toleo lililofutwa limetiwa alama na nambari ya sasa ya muamala katika sehemu ya xmax. Zaidi ya hayo, thamani hii imeandikwa juu ya zamani, tangu shughuli ya awali ilighairiwa. Na xmax_aborted biti imefutwa kwa sababu hali ya muamala wa sasa bado haijajulikana.

Toleo la kwanza la mstari sasa linarejelea la pili (t_ctid field) kama toleo jipya zaidi.

Faharasa ya pili inaonekana katika ukurasa wa faharasa na safu mlalo ya pili inarejelea toleo la pili katika ukurasa wa jedwali.

Kama ilivyo kwa ufutaji, thamani ya xmax katika toleo la kwanza la safu mlalo ni dalili kwamba safu mlalo imefungwa.

Naam, tumalizie muamala.

=> COMMIT;

Faharisi

Kufikia sasa tumezungumza tu juu ya kurasa za meza. Nini kinatokea ndani ya faharisi?

Taarifa katika kurasa za faharasa hutofautiana sana kulingana na aina mahususi ya faharasa. Na hata aina moja ya index ina aina tofauti za kurasa. Kwa mfano, mti wa B una ukurasa wa metadata na kurasa za "kawaida".

Walakini, ukurasa kawaida huwa na safu ya viashiria kwa safu na safu zenyewe (kama ukurasa wa jedwali). Kwa kuongeza, mwishoni mwa ukurasa kuna nafasi ya data maalum.

Safu katika faharasa pia inaweza kuwa na miundo tofauti sana kulingana na aina ya faharasa. Kwa mfano, kwa mti wa B, safu mlalo zinazohusiana na kurasa za majani zina thamani ya ufunguo wa kuorodhesha na rejeleo (ctid) kwa safu mlalo ya jedwali inayolingana. Kwa ujumla, index inaweza kupangwa kwa njia tofauti kabisa.

Jambo muhimu zaidi ni kwamba hakuna matoleo ya safu katika faharisi za aina yoyote. Kweli, au tunaweza kudhani kuwa kila mstari unawakilishwa na toleo moja. Kwa maneno mengine, hakuna sehemu za xmin na xmax kwenye kichwa cha safu mlalo. Tunaweza kudhani kuwa viungo kutoka kwa faharasa vinaongoza kwa matoleo yote ya jedwali la safu mlalo - kwa hivyo unaweza kubaini ni toleo gani muamala utaona kwa kutazama jedwali pekee. (Kama kawaida, huu sio ukweli wote. Katika hali nyingine, ramani ya mwonekano inaweza kuboresha mchakato, lakini tutaangalia hili kwa undani zaidi baadaye.)

Wakati huo huo, katika ukurasa wa faharisi tunapata viashiria kwa matoleo yote mawili, ya sasa na ya zamani:

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

Shughuli za mtandaoni

Kwa mazoezi, PostgreSQL hutumia uboreshaji unaoiruhusu "kuhifadhi" nambari za muamala.

Ikiwa muamala unasoma data pekee, hauathiri mwonekano wa matoleo ya safu mlalo. Kwa hivyo, mchakato wa huduma hutoa kwanza xid pepe kwa muamala. Nambari hiyo inajumuisha kitambulisho cha mchakato na nambari ya mlolongo.

Kutoa nambari hii hakuhitaji usawazishaji kati ya michakato yote na kwa hivyo ni haraka sana. Tutafahamiana na sababu nyingine ya kutumia nambari za kawaida tunapozungumza juu ya kufungia.

Nambari za kweli hazizingatiwi kwa njia yoyote katika vijipicha vya data.

Katika nyakati tofauti kwa wakati, kunaweza kuwa na shughuli za kawaida kwenye mfumo na nambari ambazo tayari zimetumika, na hii ni kawaida. Lakini nambari kama hiyo haiwezi kuandikwa kwenye kurasa za data, kwa sababu wakati ujao ukurasa unapatikana inaweza kupoteza maana yote.

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

Ikiwa muamala utaanza kubadilisha data, hupewa nambari halisi, ya kipekee ya muamala.

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

=> COMMIT;

Shughuli Zilizowekwa

Hifadhi pointi

Imefafanuliwa katika SQL kuokoa pointi (savepoint), ambayo hukuruhusu kughairi sehemu ya muamala bila kukatiza kabisa. Lakini hii haifai katika mchoro hapo juu, kwa kuwa shughuli ina hali sawa kwa mabadiliko yake yote, na kimwili hakuna data iliyorudishwa nyuma.

Ili kutekeleza utendakazi huu, muamala ulio na kihifadhi hugawanywa katika sehemu kadhaa tofauti shughuli zilizowekwa (subtransaction), hali ambayo inaweza kusimamiwa tofauti.

Miamala iliyopachikwa ina nambari yao wenyewe (juu kuliko nambari ya muamala mkuu). Hali ya shughuli zilizowekwa ni kumbukumbu kwa njia ya kawaida katika XACT, lakini hali ya mwisho inategemea hali ya shughuli kuu: ikiwa imefutwa, basi shughuli zote zilizowekwa pia zimefutwa.

Taarifa kuhusu shughuli ya kuweka kiota huhifadhiwa katika faili katika saraka ya PGDATA/pg_subtrans. Faili hufikiwa kupitia vihifadhi katika kumbukumbu iliyoshirikiwa ya mfano, iliyopangwa kwa njia sawa na vibafa vya XACT.

Usichanganye shughuli zilizowekwa na shughuli za uhuru. Shughuli za uhuru hazitegemei kila mmoja kwa njia yoyote, lakini shughuli zilizowekwa zinategemea. Hakuna shughuli za uhuru katika PostgreSQL ya kawaida, na, labda, kwa bora: zinahitajika sana, mara chache sana, na uwepo wao katika DBMS nyingine husababisha unyanyasaji, ambayo kila mtu anateseka.

Wacha tufute jedwali, anza shughuli na uingize safu:

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

Sasa hebu tuweke hatua ya kuokoa na kuingiza mstari mwingine.

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

Kumbuka kuwa chaguo za kukokotoa txid_current() hurejesha nambari kuu ya muamala, si nambari ya muamala iliyoorodheshwa.

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

Hebu turudi kwenye hatua ya kuokoa na kuingiza mstari wa tatu.

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

Katika ukurasa tunaendelea kuona safu mlalo iliyoongezwa na muamala ulioghairiwa.

Tunarekebisha mabadiliko.

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

Sasa unaweza kuona wazi kwamba kila muamala uliowekwa una hadhi yake.

Kumbuka kwamba shughuli zilizowekwa haziwezi kutumika kwa uwazi katika SQL, yaani, huwezi kuanza muamala mpya bila kukamilisha ule wa sasa. Utaratibu huu huwashwa kikamilifu wakati wa kutumia vihifadhi, na vile vile wakati wa kushughulikia vighairi vya PL/pgSQL na katika visa vingine vingi vya kigeni.

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

Makosa na atomiki ya shughuli

Ni nini hufanyika ikiwa kosa linatokea wakati wa kufanya operesheni? Kwa mfano, kama hii:

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

Kosa limetokea. Sasa shughuli hiyo inachukuliwa kuwa imekomeshwa na hakuna shughuli zinazoruhusiwa ndani yake:

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

Na hata ukijaribu kufanya mabadiliko, PostgreSQL itaripoti utoaji mimba:

=> COMMIT;
ROLLBACK

Kwa nini muamala hauwezi kuendelea baada ya kutofaulu? Ukweli ni kwamba hitilafu inaweza kutokea kwa namna ambayo tunaweza kupata sehemu ya mabadiliko - atomicity ya hata shughuli, lakini operator itakuwa imekiukwa. Kama katika mfano wetu, ambapo operator aliweza kusasisha mstari mmoja kabla ya kosa:

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

Ni lazima kusema kuwa psql ina modi ambayo bado inaruhusu shughuli kuendelea baada ya kutofaulu kana kwamba vitendo vya mwendeshaji potofu vilirudishwa nyuma.

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

Si vigumu kukisia kuwa katika hali hii, psql kwa kweli huweka uhakika wa kuhifadhi kabla ya kila amri, na ikishindikana huanzisha kurudisha nyuma kwake. Hali hii haitumiwi kwa chaguo-msingi, kwa kuwa kuweka vihifadhi (hata bila kurudishwa kwao) kunahusisha uendeshaji muhimu.

Kuendelea.

Chanzo: mapenzi.com

Kuongeza maoni