MVCC-3. Fersiynau llinynnol

Felly, rydym wedi ystyried materion yn ymwneud â inswleiddio, a gwnaeth encil tua trefnu data ar lefel isel. Ac yn olaf fe gyrhaeddon ni'r rhan fwyaf diddorol - y fersiynau llinynnol.

Teitl

Fel y dywedasom eisoes, gall pob rhes fodoli ar yr un pryd mewn sawl fersiwn yn y gronfa ddata. Mae'n rhaid gwahaniaethu rhwng un fersiwn a fersiwn arall.I'r diben hwn, mae gan bob fersiwn ddau farc sy'n pennu "amser" gweithredu'r fersiwn hon (xmin a xmax). Mewn dyfyniadau - oherwydd nid amser fel y cyfryw a ddefnyddir, ond rhifydd cynyddol arbennig. A'r rhifydd hwn yw rhif y trafodiad.

(Yn ôl yr arfer, mae'r realiti yn fwy cymhleth: ni all y rhif trafodiad gynyddu drwy'r amser oherwydd cynhwysedd didau cyfyngedig y cownter. Ond byddwn yn edrych ar y manylion hyn yn fanwl pan fyddwn yn dod i rewi.)

Pan fydd rhes yn cael ei chreu, mae xmin wedi'i osod i'r rhif trafodiad a gyhoeddodd y gorchymyn INSERT, ac mae xmax yn cael ei adael yn wag.

Pan fydd rhes yn cael ei dileu, mae gwerth xmax y fersiwn gyfredol yn cael ei farcio â rhif y trafodiad a gyflawnodd y DELETE.

Pan fydd rhes yn cael ei haddasu gan orchymyn DIWEDDARIAD, mae dau weithrediad yn cael eu perfformio mewn gwirionedd: DILEU a INSERT. Mae fersiwn gyfredol y rhes yn gosod xmax yn hafal i nifer y trafodiad a gyflawnodd y DIWEDDARIAD. Yna crëir fersiwn newydd o'r un llinyn; mae ei werth xmin yn cyd-fynd â gwerth xmax y fersiwn flaenorol.

Mae'r meysydd xmin a xmax wedi'u cynnwys ym mhennyn y fersiwn rhes. Yn ogystal â'r meysydd hyn, mae'r pennawd yn cynnwys eraill, er enghraifft:

  • Mae infomask yn gyfres o ddarnau sy'n diffinio priodweddau'r fersiwn hon. Mae cryn dipyn ohonyn nhw; Byddwn yn raddol yn ystyried y prif rai.
  • Mae ctid yn ddolen i'r fersiwn nesaf, mwy newydd o'r un llinell. Ar gyfer y fersiwn diweddaraf, mwyaf cyfredol o linyn, mae'r ctid yn cyfeirio at y fersiwn hon ei hun. Mae gan y rhif y ffurf (x,y), lle x yw rhif y dudalen, y yw'r rhif mynegai yn yr arae.
  • null didfap - Yn marcio'r colofnau hynny o fersiwn penodol sy'n cynnwys gwerth null (NULL). Nid yw NULL yn un o'r gwerthoedd math data arferol, felly rhaid storio'r priodoledd ar wahân.

O ganlyniad, mae'r pennawd yn eithaf mawr - o leiaf 23 beit ar gyfer pob fersiwn o'r llinell, ac fel arfer yn fwy oherwydd y map did NULL. Os yw'r tabl yn "gul" (hynny yw, yn cynnwys ychydig o golofnau), gall y gorben gymryd mwy na'r wybodaeth ddefnyddiol.

mewnosod

Gadewch i ni edrych yn agosach ar sut mae gweithrediadau llinynnol lefel isel yn cael eu perfformio, gan ddechrau gyda gosod.

Ar gyfer arbrofion, gadewch i ni greu tabl newydd gyda dwy golofn a mynegai ar un ohonynt:

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

Gadewch i ni fewnosod un rhes ar ôl dechrau trafodiad.

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

Dyma ein rhif trafodiad cyfredol:

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

Gadewch i ni edrych ar gynnwys y dudalen. Mae swyddogaeth heap_page_items yr estyniad pageinspect yn eich galluogi i gael gwybodaeth am awgrymiadau a fersiynau rhes:

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

Sylwch fod y gair pentwr yn PostgreSQL yn cyfeirio at dablau. Dyma ddefnydd rhyfedd arall o'r term - mae pentwr yn hysbys strwythur data, sydd heb ddim yn gyffredin â'r bwrdd. Yma y mae y gair yn cael ei ddefnyddio yn yr ystyr o " deflir pob peth at ei gilydd," yn hytrach na mynegeion gorchymynol.

Mae'r swyddogaeth yn dangos data "fel y mae", mewn fformat sy'n anodd ei ddeall. Er mwyn ei ddarganfod, byddwn yn gadael rhan yn unig o'r wybodaeth ac yn ei dehongli:

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

Dyma beth wnaethom ni:

  • Wedi ychwanegu sero at y rhif mynegai i wneud iddo edrych yr un fath â t_ctid: (rhif tudalen, rhif mynegai).
  • Wedi dehongli cyflwr y pwyntydd lp_flags. Yma mae'n "normal" - mae hyn yn golygu bod y pwyntydd mewn gwirionedd yn cyfeirio at fersiwn y llinyn. Edrychwn ar ystyron eraill yn nes ymlaen.
  • O'r holl ddarnau gwybodaeth, dim ond dau bâr sydd wedi'u nodi hyd yn hyn. Mae'r darnau xmin_committed a xmin_aborted yn nodi a yw rhif trafodiad xmin wedi'i ymrwymo (erthylu). Mae dau ddarn tebyg yn cyfeirio at rif trafodiad xmax.

Beth ydym ni'n ei weld? Pan fyddwch yn mewnosod rhes, bydd mynegai rhif 1 yn ymddangos ar dudalen y tabl, gan bwyntio at y fersiwn gyntaf a'r unig fersiwn o'r rhes.

Yn y fersiwn llinyn, mae'r maes xmin wedi'i lenwi â'r rhif trafodiad cyfredol. Mae'r trafodiad yn dal yn weithredol, felly nid yw'r darnau xmin_committed a xmin_aborted wedi'u gosod.

Mae maes ctid fersiwn y rhes yn cyfeirio at yr un rhes. Mae hyn yn golygu nad yw fersiwn mwy diweddar yn bodoli.

Mae'r maes xmax wedi'i lenwi â rhif ffug 0 oherwydd nid yw'r fersiwn hon o'r rhes wedi'i dileu ac mae'n gyfredol. Ni fydd trafodion yn talu sylw i'r rhif hwn oherwydd bod y bit xmax_aborted wedi'i osod.

Gadewch i ni gymryd un cam arall tuag at wella darllenadwyedd trwy ychwanegu darnau gwybodaeth at rifau trafodion. A gadewch i ni greu swyddogaeth, gan y bydd angen y cais arnom fwy nag unwaith:

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

Yn y ffurflen hon, mae'n llawer cliriach beth sy'n digwydd ym mhennyn fersiwn y rhes:

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

Gellir cael gwybodaeth debyg, ond llawer llai manwl, o’r tabl ei hun, gan ddefnyddio ffug-golofnau xmin a xmax:

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

Atgyweirio

Os cwblheir trafodiad yn llwyddiannus, mae angen i chi gofio ei statws - nodwch ei fod wedi ymrwymo. I wneud hyn, defnyddir strwythur o'r enw XACT (a chyn fersiwn 10 fe'i gelwid yn CLOG (log ymrwymiad) a gellir dod o hyd i'r enw hwn mewn gwahanol leoedd o hyd).

Nid tabl catalog system yw XACT; dyma'r ffeiliau yn y cyfeiriadur PGDATA/pg_xact. Mae ganddyn nhw ddau ddarn wedi'u dyrannu ar gyfer pob trafodiad: ymrwymedig ac erthylu - yn union fel ym mhennyn fersiwn y rhes. Rhennir y wybodaeth hon yn sawl ffeil er hwylustod yn unig; byddwn yn dychwelyd at y mater hwn pan fyddwn yn ystyried rhewi. Ac mae gwaith gyda'r ffeiliau hyn yn cael ei wneud fesul tudalen, fel gyda phob un arall.

Felly, pan fydd trafodiad wedi'i ymrwymo yn XACT, mae'r did ymrwymedig yn cael ei osod ar gyfer y trafodiad hwn. A dyma'r cyfan sy'n digwydd wrth ymrwymo (er nad ydym yn sôn am y log cyn-recordio eto).

Pan fydd trafodiad arall yn cyrchu'r dudalen tabl yr ydym newydd edrych arni, bydd yn rhaid iddo ateb sawl cwestiwn.

  1. A yw'r trafodiad xmin wedi'i gwblhau? Os na, yna ni ddylai'r fersiwn a grëwyd o'r llinyn fod yn weladwy.
    Gwneir y gwiriad hwn trwy edrych ar strwythur arall, sydd wedi'i leoli yn y cof a rennir o'r enghraifft ac a elwir yn ProcArray. Mae'n cynnwys rhestr o'r holl brosesau gweithredol, ac ar gyfer pob un nodir nifer ei drafodiad cyfredol (gweithredol).
  2. Os caiff ei chwblhau, sut - trwy ymrwymo neu ganslo? Os caiff ei ganslo, ni ddylai'r fersiwn rhes fod yn weladwy ychwaith.
    Dyma'n union beth yw pwrpas XACT. Ond, er bod tudalennau olaf XACT yn cael eu storio mewn byfferau yn RAM, mae'n dal yn ddrud i wirio XACT bob tro. Felly, unwaith y bydd statws y trafodiad wedi'i bennu, caiff ei ysgrifennu at y darnau xmin_committed a xmin_aborted o'r fersiwn llinynnol. Os gosodir un o'r darnau hyn, yna ystyrir bod cyflwr y trafodiad xmin yn hysbys ac ni fydd yn rhaid i'r trafodiad nesaf gael mynediad at XACT.

Pam nad yw'r darnau hyn yn cael eu gosod gan y trafodiad ei hun yn gwneud y mewnosodiad? Pan fydd mewnosodiad yn digwydd, nid yw'r trafodiad yn gwybod eto a fydd yn llwyddo. Ac ar hyn o bryd o ymrwymo, nid yw'n glir bellach ym mha linellau y newidiwyd tudalennau. Efallai bod llawer o dudalennau o'r fath, ac mae'n amhroffidiol eu cofio. Yn ogystal, gall rhai tudalennau gael eu troi allan o'r storfa byffer i ddisg; byddai eu darllen eto i newid y darnau yn arafu'r ymrwymiad yn sylweddol.

Anfantais yr arbedion yw y gall unrhyw drafodiad (hyd yn oed un sy'n perfformio darlleniad syml - SELECT) ddechrau newid tudalennau data yn y storfa byffer ar ôl newidiadau.

Felly, gadewch i ni drwsio'r newid.

=> COMMIT;

Nid oes dim wedi newid ar y dudalen (ond rydym yn gwybod bod statws y trafodiad eisoes wedi'i gofnodi yn XACT):

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

Nawr bydd yn rhaid i'r trafodiad sy'n cyrchu'r dudalen gyntaf bennu statws y trafodiad xmin a'i ysgrifennu at y darnau gwybodaeth:

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

Tynnu

Pan fydd rhes yn cael ei dileu, mae rhif y trafodiad dileu cyfredol yn cael ei ysgrifennu i faes xmax y fersiwn gyfredol, ac mae'r bit xmax_aborted yn cael ei glirio.

Sylwch fod gwerth gosod xmax sy'n cyfateb i'r trafodiad gweithredol yn gweithredu fel clo rhes. Os yw trafodiad arall eisiau diweddaru neu ddileu'r rhes hon, bydd yn cael ei orfodi i aros i drafodiad xmax gael ei gwblhau. Byddwn yn siarad mwy am rwystro yn nes ymlaen. Am y tro, rydyn ni'n nodi bod nifer y cloeon rhes yn ddiderfyn. Nid ydynt yn cymryd lle yn RAM ac nid yw perfformiad system yn dioddef o'u nifer. Yn wir, mae anfanteision eraill i drafodion “hir”, ond mwy am hynny yn nes ymlaen.

Gadewch i ni ddileu'r llinell.

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

Gwelwn fod rhif y trafodiad wedi'i ysgrifennu yn y maes xmax, ond nid yw'r darnau gwybodaeth wedi'u gosod:

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

Canslo

Mae erthylu newidiadau yn gweithio'n debyg i ymrwymo, dim ond yn XACT y did erthylu sy'n cael ei osod ar gyfer y trafodiad. Mae dadwneud mor gyflym ag ymrwymo. Er bod y gorchymyn yn cael ei alw'n ROLLBACK, nid yw newidiadau'n cael eu rholio yn ôl: mae popeth y llwyddodd y trafodiad i'w newid yn y tudalennau data yn parhau heb ei newid.

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

Pan gyrchir y dudalen, bydd y statws yn cael ei wirio a bydd y bit awgrym xmax_aborted yn cael ei osod i fersiwn y rhes. Mae'r rhif xmax ei hun yn aros ar y dudalen, ond ni fydd neb yn edrych arno.

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

Diweddariad

Mae'r diweddariad yn gweithio fel pe bai'n dileu'r fersiwn gyfredol o'r rhes yn gyntaf ac yna'n mewnosod un newydd.

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

Mae'r ymholiad yn cynhyrchu un llinell (fersiwn newydd):

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

Ond ar y dudalen gwelwn y ddau fersiwn:

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

Mae'r fersiwn sydd wedi'i dileu wedi'i marcio â'r rhif trafodiad cyfredol yn y maes xmax. Ar ben hynny, mae'r gwerth hwn wedi'i ysgrifennu dros yr hen un, ers i'r trafodiad blaenorol gael ei ganslo. Ac mae'r bit xmax_aborted yn cael ei glirio oherwydd nad yw statws y trafodiad cyfredol yn hysbys eto.

Mae fersiwn gyntaf y llinell bellach yn cyfeirio at yr ail (maes t_ctid) fel yr un mwy newydd.

Mae ail fynegai yn ymddangos yn y dudalen mynegai ac mae ail res yn cyfeirio at yr ail fersiwn ar dudalen y tabl.

Yn union fel gyda dileu, mae'r gwerth xmax yn fersiwn gyntaf y rhes yn arwydd bod y rhes wedi'i chloi.

Wel, gadewch i ni gwblhau'r trafodiad.

=> COMMIT;

Mynegeion

Hyd yn hyn dim ond am dudalennau tabl yr ydym wedi siarad. Beth sy'n digwydd y tu mewn i'r mynegeion?

Mae'r wybodaeth ar dudalennau mynegai yn amrywio'n fawr yn dibynnu ar y math penodol o fynegai. Ac mae gan hyd yn oed un math o fynegai wahanol fathau o dudalennau. Er enghraifft, mae gan goeden B dudalen metadata a thudalennau “rheolaidd”.

Fodd bynnag, fel arfer mae gan y dudalen amrywiaeth o awgrymiadau i'r rhesi a'r rhesi eu hunain (yn union fel tudalen tabl). Yn ogystal, ar ddiwedd y dudalen mae lle ar gyfer data arbennig.

Gall rhesi mewn mynegeion hefyd fod â strwythurau gwahanol iawn yn dibynnu ar y math o fynegai. Er enghraifft, ar gyfer coeden B, mae'r rhesi sy'n gysylltiedig â thudalennau dail yn cynnwys y gwerth allwedd mynegeio a chyfeiriad (ctid) i'r rhes tabl cyfatebol. Yn gyffredinol, gellir strwythuro'r mynegai mewn ffordd hollol wahanol.

Y pwynt pwysicaf yw nad oes fersiynau rhes mewn mynegeion o unrhyw fath. Wel, neu gallwn dybio bod pob llinell yn cael ei chynrychioli gan un fersiwn yn union. Mewn geiriau eraill, nid oes meysydd xmin a xmax ym mhennyn rhes y mynegai. Gallwn dybio bod dolenni o'r mynegai yn arwain at bob fersiwn tabl o'r rhesi - fel y gallwch chi ddarganfod pa fersiwn y bydd y trafodiad yn ei weld dim ond trwy edrych ar y tabl. (Fel bob amser, nid dyma'r gwir i gyd. Mewn rhai achosion, gall y map gwelededd wneud y gorau o'r broses, ond byddwn yn edrych ar hyn yn fanylach yn nes ymlaen.)

Ar yr un pryd, yn y dudalen fynegai rydym yn dod o hyd i awgrymiadau i'r ddau fersiwn, yr un gyfredol a'r hen un:

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

Trafodion rhithwir

Yn ymarferol, mae PostgreSQL yn defnyddio optimeiddiadau sy'n caniatáu iddo “arbed” rhifau trafodion.

Os yw trafodiad yn darllen data yn unig, nid yw'n cael unrhyw effaith ar welededd fersiynau rhes. Felly, mae'r broses gwasanaeth yn rhoi xid rhithwir i'r trafodiad yn gyntaf. Mae'r rhif yn cynnwys ID proses a rhif dilyniant.

Nid yw cyhoeddi'r rhif hwn yn gofyn am gydamseru rhwng yr holl brosesau ac felly mae'n gyflym iawn. Byddwn yn dod yn gyfarwydd â rheswm arall dros ddefnyddio rhifau rhithwir pan fyddwn yn sôn am rewi.

Nid yw rhifau rhithwir yn cael eu hystyried mewn unrhyw ffordd mewn cipluniau data.

Ar wahanol adegau, mae'n bosibl iawn y bydd trafodion rhithwir yn y system gyda rhifau sydd eisoes wedi'u defnyddio, ac mae hyn yn normal. Ond ni ellir ysgrifennu rhif o'r fath ar dudalennau data, oherwydd y tro nesaf y bydd y dudalen yn cael ei chyrchu gall golli pob ystyr.

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

Os bydd trafodiad yn dechrau newid data, rhoddir rhif trafodiad real, unigryw iddo.

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

=> COMMIT;

Trafodion nythu

Arbed pwyntiau

Wedi'i ddiffinio yn SQL arbed pwyntiau (savepoint), sy'n eich galluogi i ganslo rhan o drafodiad heb dorri ar ei draws yn gyfan gwbl. Ond nid yw hyn yn cyd-fynd â'r diagram uchod, gan fod gan y trafodiad yr un statws ar gyfer ei holl newidiadau, ac yn ffisegol nid oes unrhyw ddata yn cael ei rolio'n ôl.

Er mwyn gweithredu'r swyddogaeth hon, mae trafodiad gydag arbediad yn cael ei rannu'n sawl un ar wahân trafodion nythu (is-drafodiad), y gellir rheoli ei statws ar wahân.

Mae gan drafodion nythu eu rhif eu hunain (yn uwch na rhif y prif drafodiad). Mae statws trafodion nythu yn cael ei gofnodi yn y ffordd arferol yn XACT, ond mae'r statws terfynol yn dibynnu ar statws y prif drafodiad: os caiff ei ganslo, yna mae'r holl drafodion nythu hefyd yn cael eu canslo.

Mae gwybodaeth am nythu trafodion yn cael ei storio mewn ffeiliau yn y cyfeiriadur PGDATA/pg_subtrans. Ceir mynediad i ffeiliau trwy glustogau yng nghof a rennir yr enghraifft, wedi'u trefnu yn yr un modd â byfferau XACT.

Peidiwch â drysu rhwng trafodion nythu a thrafodion ymreolaethol. Nid yw trafodion ymreolaethol yn dibynnu ar ei gilydd mewn unrhyw ffordd, ond mae trafodion nythu yn dibynnu ar ei gilydd. Nid oes unrhyw drafodion ymreolaethol yn PostgreSQL rheolaidd, ac, efallai, am y gorau: anaml iawn, iawn y mae eu hangen, ac mae eu presenoldeb mewn DBMS eraill yn ysgogi cam-drin, y mae pawb wedyn yn dioddef ohono.

Gadewch i ni glirio'r bwrdd, cychwyn trafodiad a mewnosod y rhes:

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

Nawr gadewch i ni roi pwynt arbed a mewnosod llinell arall.

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

Sylwch fod y ffwythiant txid_current() yn dychwelyd y prif rif trafodiad, nid y rhif trafodiad nythu.

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

Gadewch i ni rolio'n ôl i'r pwynt arbed a mewnosod y drydedd llinell.

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

Yn y dudalen rydym yn parhau i weld y rhes yn cael ei hychwanegu gan y trafodiad nythu canslo.

Rydym yn trwsio'r newidiadau.

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

Nawr gallwch chi weld yn glir bod gan bob trafodiad nythu ei statws ei hun.

Sylwch na ellir defnyddio trafodion nythu yn benodol yn SQL, hynny yw, ni allwch ddechrau trafodiad newydd heb gwblhau'r un cyfredol. Mae'r mecanwaith hwn yn cael ei weithredu'n ymhlyg wrth ddefnyddio mannau arbed, yn ogystal ag wrth drin eithriadau PL/pgSQL ac mewn nifer o achosion eraill, mwy egsotig.

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

Gwallau ac atomigedd gweithrediadau

Beth sy'n digwydd os bydd gwall yn digwydd wrth gyflawni llawdriniaeth? Er enghraifft, fel hyn:

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

Mae gwall wedi digwydd. Nawr ystyrir bod y trafodiad wedi'i erthylu ac ni chaniateir unrhyw weithrediadau ynddo:

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

A hyd yn oed os ceisiwch ymrwymo'r newidiadau, bydd PostgreSQL yn adrodd am erthyliad:

=> COMMIT;
ROLLBACK

Pam na all trafodiad barhau ar ôl methiant? Y ffaith yw y gallai gwall godi yn y fath fodd fel y byddem yn cael mynediad at ran o'r newidiadau - atomigrwydd nid hyd yn oed y trafodiad, ond byddai'r gweithredwr yn cael ei dorri. Fel yn ein hesiampl, lle llwyddodd y gweithredwr i ddiweddaru un llinell cyn y gwall:

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

Rhaid dweud bod gan psql fodd sy'n dal i ganiatáu i'r trafodiad barhau ar ôl methiant fel pe bai gweithredoedd y gweithredwr gwallus yn cael eu rholio yn ôl.

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

Nid yw'n anodd dyfalu, yn y modd hwn, bod psql mewn gwirionedd yn rhoi pwynt arbed ymhlyg cyn pob gorchymyn, a rhag ofn y bydd methiant yn cychwyn dychwelyd iddo. Ni ddefnyddir y modd hwn yn ddiofyn, gan fod gosod pwyntiau arbed (hyd yn oed heb ddychwelyd atynt) yn golygu gorbenion sylweddol.

Parhad.

Ffynhonnell: hab.com

Ychwanegu sylw