TÄtad, mÄs esam izskatÄ«juÅ”i jautÄjumus, kas saistÄ«ti ar
Virsraksts
KÄ jau teicÄm, katra rinda vienlaikus var atrasties datu bÄzÄ vairÄkÄs versijÄs. Viena versija ir kaut kÄ jÄatŔķir no otras Å im nolÅ«kam katrai versijai ir divas atzÄ«mes, kas nosaka Ŕīs versijas darbÄ«bas ālaikuā (xmin un xmax). PÄdiÅÄs - jo tiek izmantots nevis laiks kÄ tÄds, bet gan Ä«paÅ”s pieaugoÅ”ais skaitÄ«tÄjs. Un Å”is skaitÄ«tÄjs ir darÄ«juma numurs.
(KÄ parasti, realitÄte ir sarežģītÄka: skaitÄ«tÄja ierobežotÄs bitu ietilpÄ«bas dÄļ transakcijas numurs nevar visu laiku palielinÄties. Bet mÄs Ŕīs detaļas aplÅ«kosim sÄ«kÄk, kad nonÄksim lÄ«dz iesaldÄÅ”anai.)
Kad rinda ir izveidota, xmin tiek iestatÄ«ts uz darÄ«juma numuru, kas izdeva komandu INSERT, un lauks xmax tiek atstÄts tukÅ”s.
DzÄÅ”ot rindu, paÅ”reizÄjÄs versijas xmax vÄrtÄ«ba tiek atzÄ«mÄta ar tÄs darÄ«juma numuru, kurÄ tika veikta DELETE.
Kad rinda tiek modificÄta ar komandu UPDATE, faktiski tiek veiktas divas darbÄ«bas: DELETE un INSERT. PaÅ”reizÄjÄ rindas versijÄ xmax ir vienÄds ar transakcijas numuru, kas veica ATJAUNINÄJUMU. PÄc tam tiek izveidota jauna tÄs paÅ”as virknes versija; tÄ xmin vÄrtÄ«ba sakrÄ«t ar iepriekÅ”ÄjÄs versijas xmax vÄrtÄ«bu.
Lauki xmin un xmax ir iekļauti rindas versijas galvenÄ. Papildus Å”iem laukiem galvenÄ ir arÄ« citi lauki, piemÄram:
- infomask ir bitu sÄrija, kas nosaka Ŕīs versijas rekvizÄ«tus. ViÅu ir diezgan daudz; MÄs pakÄpeniski apsvÄrsim galvenos.
- ctid ir saite uz nÄkamo, jaunÄku tÄs paÅ”as rindas versiju. JaunÄkajai virknes versijai ctid attiecas uz Å”o versiju. Skaitlim ir forma (x,y), kur x ir lapas numurs, y ir indeksa numurs masÄ«vÄ.
- null bitmap ā atzÄ«mÄ tÄs dotÄs versijas kolonnas, kurÄs ir nulles vÄrtÄ«ba (NULL). NULL nav viena no parastajÄm datu tipa vÄrtÄ«bÄm, tÄpÄc atribÅ«ts ir jÄuzglabÄ atseviŔķi.
RezultÄtÄ galvene ir diezgan liela - vismaz 23 baiti katrai rindas versijai, un parasti vairÄk, pateicoties NULL bitkartei. Ja tabula ir "Å”aura" (tas ir, tajÄ ir maz kolonnu), pieskaitÄmÄs izmaksas var aizÅemt vairÄk nekÄ noderÄ«ga informÄcija.
ievietot
SÄ«kÄk apskatÄ«sim, kÄ tiek veiktas zema lÄ«meÅa virkÅu darbÄ«bas, sÄkot ar ievietoÅ”anu.
Eksperimentiem izveidosim jaunu tabulu ar divÄm kolonnÄm un indeksu vienÄ no tÄm:
=> CREATE TABLE t(
id serial,
s text
);
=> CREATE INDEX ON t(s);
PÄc darÄ«juma uzsÄkÅ”anas ievietosim vienu rindu.
=> BEGIN;
=> INSERT INTO t(s) VALUES ('FOO');
Å eit ir mÅ«su paÅ”reizÄjÄ darÄ«juma numurs:
=> SELECT txid_current();
txid_current
--------------
3664
(1 row)
ApskatÄ«sim lapas saturu. PaplaÅ”inÄjuma pageinspect funkcija heap_page_items ļauj iegÅ«t informÄciju par rÄdÄ«tÄjiem un rindu versijÄm:
=> 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
Å
emiet vÄrÄ, ka vÄrds kaudze programmÄ PostgreSQL attiecas uz tabulÄm. Å is ir vÄl viens dÄ«vains Ŕī termina lietojums - ir zinÄms kaudze
Funkcija parÄda datus ātÄdos, kÄdi irā grÅ«ti saprotamÄ formÄtÄ. Lai to noskaidrotu, mÄs atstÄsim tikai daļu informÄcijas un atÅ”ifrÄsim to:
=> 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)
LÅ«k, ko mÄs izdarÄ«jÄm:
- Indeksa numuram ir pievienota nulle, lai tas izskatÄ«tos tÄpat kÄ t_ctid: (lapas numurs, indeksa numurs).
- AtÅ”ifrÄts rÄdÄ«tÄja lp_flags stÄvoklis. Å eit tas ir "normÄls" - tas nozÄ«mÄ, ka rÄdÄ«tÄjs faktiski attiecas uz virknes versiju. Citas nozÄ«mes apskatÄ«sim vÄlÄk.
- No visiem informÄcijas bitiem lÄ«dz Å”im ir identificÄti tikai divi pÄri. Biti xmin_committed un xmin_aborted norÄda, vai transakcijas numurs xmin ir veikts (pÄrtraukts). Divi lÄ«dzÄ«gi biti attiecas uz darÄ«juma numuru xmax.
Ko mÄs redzam? Ievietojot rindu, tabulas lapÄ parÄdÄ«sies indeksa numurs 1, kas norÄda uz rindas pirmo un vienÄ«go versiju.
Virknes versijÄ lauks xmin ir aizpildÄ«ts ar paÅ”reizÄjÄ darÄ«juma numuru. DarÄ«jums joprojÄm ir aktÄ«vs, tÄpÄc biti xmin_committed un xmin_aborted nav iestatÄ«ti.
Rindas versijas ctid lauks attiecas uz to paÅ”u rindu. Tas nozÄ«mÄ, ka jaunÄka versija nepastÄv.
Lauks xmax ir aizpildÄ«ts ar fiktÄ«vu skaitli 0, jo Ŕī rindas versija nav dzÄsta un ir aktuÄla. DarÄ«jumos Å”im skaitlim netiks pievÄrsta uzmanÄ«ba, jo ir iestatÄ«ts bits xmax_aborted.
Spersim vÄl vienu soli lasÄmÄ«bas uzlaboÅ”anai, pievienojot informÄcijas bitus darÄ«jumu numuriem. Un izveidosim funkciju, jo pieprasÄ«jums mums bÅ«s vajadzÄ«gs vairÄk nekÄ vienu reizi:
=> 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;
Å ajÄ formÄ ir daudz skaidrÄk redzams, kas notiek rindas versijas galvenÄ:
=> SELECT * FROM heap_page('t',0);
ctid | state | xmin | xmax | t_ctid
-------+--------+------+-------+--------
(0,1) | normal | 3664 | 0 (a) | (0,1)
(1 row)
LÄ«dzÄ«gu, bet ievÄrojami mazÄk detalizÄtu informÄciju var iegÅ«t no paÅ”as tabulas, izmantojot pseidokolonnas xmin un xmax:
=> SELECT xmin, xmax, * FROM t;
xmin | xmax | id | s
------+------+----+-----
3664 | 0 | 1 | FOO
(1 row)
FiksÄcija
Ja darÄ«jums ir veiksmÄ«gi pabeigts, jums ir jÄatceras tÄ statuss - Åemiet vÄrÄ, ka tas ir veikts. Lai to izdarÄ«tu, tiek izmantota struktÅ«ra ar nosaukumu XACT (un pirms 10. versijas to sauca CLOG (commit log), un Å”is nosaukums joprojÄm ir atrodams dažÄdÄs vietÄs).
XACT nav sistÄmas kataloga tabula; tie ir faili direktorijÄ PGDATA/pg_xact. Katram darÄ«jumam ir divi biti: veikts un pÄrtraukts ā tÄpat kÄ rindas versijas galvenÄ. Å Ä« informÄcija ir sadalÄ«ta vairÄkos failos tikai ÄrtÄ«bas labad; mÄs atgriezÄ«simies pie Ŕīs problÄmas, kad apsvÄrsim iesaldÄÅ”anu. Un darbs ar Å”iem failiem tiek veikts pa lappusei, tÄpat kÄ ar visiem citiem.
TÄtad, kad transakcija tiek veikta XACT, Å”im darÄ«jumam tiek iestatÄ«ts piesaistÄ«tais bits. Un tas ir viss, kas notiek apÅemÅ”anÄs laikÄ (lai gan mÄs vÄl nerunÄjam par iepriekÅ”Äjas ierakstÄ«Å”anas žurnÄlu).
Kad cits darÄ«jums piekļūs tabulas lapai, kuru tikko apskatÄ«jÄm, tai bÅ«s jÄatbild uz vairÄkiem jautÄjumiem.
- Vai xmin darÄ«jums ir pabeigts? Ja nÄ, izveidotajai virknes versijai nevajadzÄtu bÅ«t redzamai.
Å Ä« pÄrbaude tiek veikta, aplÅ«kojot citu struktÅ«ru, kas atrodas instances koplietotajÄ atmiÅÄ un tiek saukta par ProcArray. TajÄ ir visu aktÄ«vo procesu saraksts, un katram ir norÄdÄ«ts tÄ paÅ”reizÄjÄs (aktÄ«vÄs) transakcijas numurs. - Ja pabeigts, tad kÄ - uzÅemoties vai atceļot? Ja tas tiek atcelts, tad arÄ« rindas versijai nevajadzÄtu bÅ«t redzamai.
Tas ir tieÅ”i tas, kam XACT ir paredzÄts. Bet, lai gan pÄdÄjÄs XACT lapas tiek glabÄtas RAM buferos, joprojÄm ir dÄrgi pÄrbaudÄ«t XACT katru reizi. TÄpÄc, tiklÄ«dz ir noteikts darÄ«juma statuss, tas tiek ierakstÄ«ts virknes versijas bitos xmin_committed un xmin_aborted. Ja ir iestatÄ«ts kÄds no Å”iem bitiem, transakcijas stÄvoklis xmin tiek uzskatÄ«ts par zinÄmu un nÄkamajam darÄ«jumam nebÅ«s jÄpiekļūst XACT.
KÄpÄc Å”ie biti netiek iestatÄ«ti ar transakciju, kas veic ievietoÅ”anu? Kad notiek ievietoÅ”ana, darÄ«jums vÄl nezina, vai tas izdosies. Un apÅemÅ”anÄs brÄ«dÄ« vairs nav skaidrs, kurÄs rindÄs kurÄ lapÄs tika mainÄ«tas. Å Ädu lapu var bÅ«t daudz, un to iegaumÄÅ”ana ir neizdevÄ«ga. TurklÄt dažas lapas var izlikt no bufera keÅ”atmiÅas uz disku; to lasÄ«Å”ana vÄlreiz, lai mainÄ«tu bitus, ievÄrojami palÄninÄtu saistÄ«bu izpildi.
IetaupÄ«juma mÄ«nuss ir tÄds, ka pÄc izmaiÅÄm jebkurÅ” darÄ«jums (pat tÄds, kas veic vienkÄrÅ”u lasÄ«Å”anu ā SELECT) var sÄkt mainÄ«t datu lapas bufera keÅ”atmiÅÄ.
TÄtad, labosim izmaiÅas.
=> COMMIT;
LapÄ nekas nav mainÄ«jies (taÄu mÄs zinÄm, ka darÄ«juma statuss jau ir reÄ£istrÄts XACT):
=> SELECT * FROM heap_page('t',0);
ctid | state | xmin | xmax | t_ctid
-------+--------+------+-------+--------
(0,1) | normal | 3664 | 0 (a) | (0,1)
(1 row)
Tagad darÄ«jumam, kas vispirms piekļūst lapai, bÅ«s jÄnosaka xmin darÄ«juma statuss un jÄieraksta tas informÄcijas bitos:
=> 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)
PÄrcelÅ”anÄs
Kad rinda tiek dzÄsta, paÅ”reizÄjÄs versijas laukÄ xmax tiek ierakstÄ«ts paÅ”reizÄjÄs dzÄÅ”anas transakcijas numurs un tiek notÄ«rÄ«ts bits xmax_aborted.
Å emiet vÄrÄ, ka iestatÄ«tÄ xmax vÄrtÄ«ba, kas atbilst aktÄ«vajam darÄ«jumam, darbojas kÄ rindas bloÄ·ÄtÄjs. Ja cits darÄ«jums vÄlas atjauninÄt vai dzÄst Å”o rindu, tas bÅ«s spiests gaidÄ«t, lÄ«dz transakcija xmax tiks pabeigta. VairÄk par bloÄ·ÄÅ”anu runÄsim vÄlÄk. PagaidÄm mÄs tikai atzÄ«mÄjam, ka rindu slÄdzeÅu skaits ir neierobežots. Tie neaizÅem vietu RAM, un sistÄmas veiktspÄja necieÅ” no to skaita. Tiesa, āilgiemā darÄ«jumiem ir arÄ« citi trÅ«kumi, bet par to vÄlÄk.
IzdzÄsÄ«sim rindu.
=> BEGIN;
=> DELETE FROM t;
=> SELECT txid_current();
txid_current
--------------
3665
(1 row)
MÄs redzam, ka darÄ«juma numurs ir ierakstÄ«ts laukÄ xmax, bet informÄcijas biti nav iestatÄ«ti:
=> SELECT * FROM heap_page('t',0);
ctid | state | xmin | xmax | t_ctid
-------+--------+----------+------+--------
(0,1) | normal | 3664 (c) | 3665 | (0,1)
(1 row)
AtcelŔana
IzmaiÅu pÄrtraukÅ”ana darbojas lÄ«dzÄ«gi kÄ apÅemÅ”anÄs, tikai XACT transakcijai tiek iestatÄ«ts pÄrtrauktais bits. AtcelÅ”ana ir tikpat Ätra kÄ apÅemÅ”anÄs. Lai gan komanda tiek saukta ROLLBACK, izmaiÅas netiek atgrieztas: viss, ko darÄ«jumam izdevÄs mainÄ«t datu lapÄs, paliek nemainÄ«gs.
=> ROLLBACK;
=> SELECT * FROM heap_page('t',0);
ctid | state | xmin | xmax | t_ctid
-------+--------+----------+------+--------
(0,1) | normal | 3664 (c) | 3665 | (0,1)
(1 row)
Kad lapa tiek atvÄrta, statuss tiks pÄrbaudÄ«ts, un mÄjienu bitam xmax_aborted tiks iestatÄ«ta rindas versija. Pats xmax numurs paliek lapÄ, bet neviens uz to neskatÄ«sies.
=> 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)
ModernizÄt
AtjauninÄjums darbojas tÄ, it kÄ vispirms tiktu izdzÄsta paÅ”reizÄjÄ rindas versija un pÄc tam ievietota jauna.
=> BEGIN;
=> UPDATE t SET s = 'BAR';
=> SELECT txid_current();
txid_current
--------------
3666
(1 row)
VaicÄjums rada vienu rindiÅu (jauna versija):
=> SELECT * FROM t;
id | s
----+-----
1 | BAR
(1 row)
Bet lapÄ mÄs redzam abas versijas:
=> 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)
IzdzÄstÄ versija ir atzÄ«mÄta ar paÅ”reizÄjÄ darÄ«juma numuru laukÄ xmax. TurklÄt Ŕī vÄrtÄ«ba tiek rakstÄ«ta virs vecÄ, jo iepriekÅ”Äjais darÄ«jums tika atcelts. Un bits xmax_aborted tiek notÄ«rÄ«ts, jo paÅ”reizÄjÄ darÄ«juma statuss vÄl nav zinÄms.
PirmÄ rindas versija tagad attiecas uz otro (lauks t_ctid) kÄ jaunÄko versiju.
Otrais rÄdÄ«tÄjs parÄdÄs rÄdÄ«tÄja lapÄ, un otrÄ rinda norÄda uz otro versiju tabulas lapÄ.
TÄpat kÄ dzÄÅ”anas gadÄ«jumÄ, xmax vÄrtÄ«ba rindas pirmajÄ versijÄ norÄda, ka rinda ir bloÄ·Äta.
Nu, pabeigsim darījumu.
=> COMMIT;
Indeksi
LÄ«dz Å”im esam runÄjuÅ”i tikai par tabulu lapÄm. Kas notiek indeksos?
InformÄcija rÄdÄ«tÄja lapÄs ievÄrojami atŔķiras atkarÄ«bÄ no konkrÄtÄ rÄdÄ«tÄja veida. Un pat viena veida indeksam ir dažÄda veida lapas. PiemÄram, B kokam ir metadatu lapa un āparastÄsā lapas.
TomÄr lapÄ parasti ir masÄ«vs norÄdes uz rindÄm un paÅ”Äm rindÄm (tÄpat kÄ tabulas lapÄ). TurklÄt lapas beigÄs ir vieta Ä«paÅ”iem datiem.
ArÄ« indeksu rindÄm var bÅ«t ļoti atŔķirÄ«ga struktÅ«ra atkarÄ«bÄ no indeksa veida. PiemÄram, B kokam rindas, kas saistÄ«tas ar lapu lapÄm, satur indeksÄÅ”anas atslÄgas vÄrtÄ«bu un atsauci (ctid) uz atbilstoÅ”o tabulas rindu. KopumÄ indeksu var strukturÄt pavisam citÄdi.
VissvarÄ«gÄkais ir tas, ka neviena veida indeksos nav rindu versiju. Nu, vai arÄ« mÄs varam pieÅemt, ka katra rinda ir attÄlota tieÅ”i ar vienu versiju. Citiem vÄrdiem sakot, indeksa rindas galvenÄ nav lauku xmin un xmax. MÄs varam pieÅemt, ka saites no indeksa ved uz visÄm rindu tabulu versijÄm - tÄpÄc jÅ«s varat noskaidrot, kuru versiju darÄ«jums redzÄs, tikai apskatot tabulu. (KÄ vienmÄr, tÄ nav visa patiesÄ«ba. Dažos gadÄ«jumos redzamÄ«bas karte var optimizÄt procesu, taÄu mÄs to aplÅ«kosim vÄlÄk.)
TajÄ paÅ”Ä laikÄ rÄdÄ«tÄja lapÄ mÄs atrodam norÄdes uz abÄm versijÄm, gan paÅ”reizÄjo, gan veco:
=> SELECT itemoffset, ctid FROM bt_page_items('t_s_idx',1);
itemoffset | ctid
------------+-------
1 | (0,2)
2 | (0,1)
(2 rows)
VirtuÄlie darÄ«jumi
PraksÄ PostgreSQL izmanto optimizÄcijas, kas ļauj āsaglabÄtā darÄ«jumu numurus.
Ja darÄ«jums tikai nolasa datus, tas neietekmÄ rindu versiju redzamÄ«bu. TÄpÄc apkalpoÅ”anas process vispirms darÄ«jumam izsniedz virtuÄlo xid. Numurs sastÄv no procesa ID un kÄrtas numura.
Å Ä« numura izsniegÅ”anai nav nepiecieÅ”ama sinhronizÄcija starp visiem procesiem, un tÄpÄc tas ir ļoti Ätrs. MÄs iepazÄ«simies ar vÄl vienu virtuÄlo numuru izmantoÅ”anas iemeslu, runÄjot par iesaldÄÅ”anu.
Datu momentuzÅÄmumos virtuÄlie skaitļi nekÄdÄ veidÄ netiek Åemti vÄrÄ.
DažÄdos brīžos sistÄmÄ var bÅ«t virtuÄli darÄ«jumi ar numuriem, kas jau ir izmantoti, un tas ir normÄli. Bet Å”Ädu numuru nevar ierakstÄ«t datu lapÄs, jo nÄkamajÄ reizÄ, kad lapa tiek atvÄrta, tas var zaudÄt visu nozÄ«mi.
=> BEGIN;
=> SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
(1 row)
Ja darÄ«jums sÄk mainÄ«t datus, tam tiek pieŔķirts reÄls unikÄls darÄ«juma numurs.
=> UPDATE accounts SET amount = amount - 1.00;
=> SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
3667
(1 row)
=> COMMIT;
Ligzdotie darījumi
SaglabÄjiet punktus
DefinÄts SQL saglabÄ punktus (savepoint), kas ļauj atcelt daļu darÄ«juma, nepÄrtraucot to pilnÄ«bÄ. Bet tas neiekļaujas iepriekÅ” minÄtajÄ diagrammÄ, jo darÄ«jumam ir vienÄds statuss visÄm tÄ izmaiÅÄm un fiziski dati netiek atgriezti.
Lai ieviestu Å”o funkcionalitÄti, darÄ«jums ar saglabÄÅ”anas punktu tiek sadalÄ«ts vairÄkos atseviŔķos ligzdoti darÄ«jumi (apakÅ”darÄ«jums), kura statusu var pÄrvaldÄ«t atseviŔķi.
Ligzdotajiem darÄ«jumiem ir savs numurs (lielÄks nekÄ galvenÄ darÄ«juma numurs). Ligzdoto transakciju statuss tiek fiksÄts parastajÄ veidÄ XACT, bet gala statuss ir atkarÄ«gs no galvenÄ darÄ«juma statusa: ja tas tiek atcelts, tad tiek atcelti arÄ« visi ligzdotie darÄ«jumi.
InformÄcija par darÄ«jumu ligzdoÅ”anu tiek glabÄta failos direktorijÄ PGDATA/pg_subtrans. Failiem var piekļūt, izmantojot gadÄ«juma koplietotÄs atmiÅas buferus, kas sakÄrtoti tÄpat kÄ XACT buferi.
Nejauciet ligzdotos darÄ«jumus ar autonomiem darÄ«jumiem. Autonomie darÄ«jumi nekÄdÄ veidÄ nav atkarÄ«gi viens no otra, bet ligzdotie darÄ«jumi ir atkarÄ«gi. ParastÄ PostgreSQL nav autonomu darÄ«jumu, un, iespÄjams, labÄkajÄ gadÄ«jumÄ: tie ir nepiecieÅ”ami ļoti, ļoti reti, un to klÄtbÅ«tne citÄs DBVS provocÄ Ä¼aunprÄtÄ«gu izmantoÅ”anu, no kuras visi cieÅ”.
NotÄ«rÄ«sim tabulu, sÄksim darÄ«jumu un ievietosim rindu:
=> 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)
Tagad ievietosim saglabÄÅ”anas punktu un ievietosim citu rindiÅu.
=> SAVEPOINT sp;
=> INSERT INTO t(s) VALUES ('XYZ');
=> SELECT txid_current();
txid_current
--------------
3669
(1 row)
Å emiet vÄrÄ, ka funkcija txid_current() atgriež galvenÄ darÄ«juma numuru, nevis ligzdotÄ darÄ«juma numuru.
=> 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)
AtgriezÄ«simies lÄ«dz saglabÄÅ”anas punktam un ievietosim treÅ”o rindiÅu.
=> 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)
LapÄ mÄs turpinÄm redzÄt rindu, ko pievienoja atceltais ligzdotais darÄ«jums.
MÄs labojam izmaiÅas.
=> 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)
Tagad jÅ«s varat skaidri redzÄt, ka katram ligzdotajam darÄ«jumam ir savs statuss.
Å emiet vÄrÄ, ka ligzdotas transakcijas nevar izmantot tieÅ”i SQL, tas ir, jÅ«s nevarat sÄkt jaunu transakciju, nepabeidzot paÅ”reizÄjo. Å is mehÄnisms tiek aktivizÄts netieÅ”i, izmantojot saglabÄÅ”anas punktus, kÄ arÄ« apstrÄdÄjot PL/pgSQL izÅÄmumus un vairÄkos citos, eksotiskÄkos gadÄ«jumos.
=> BEGIN;
BEGIN
=> BEGIN;
WARNING: there is already a transaction in progress
BEGIN
=> COMMIT;
COMMIT
=> COMMIT;
WARNING: there is no transaction in progress
COMMIT
Kļūdas un darbÄ«bu atomitÄte
Kas notiek, ja darbÄ«bas laikÄ rodas kļūda? PiemÄram, Å”Ädi:
=> 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
RadÄs kļūda. Tagad darÄ«jums tiek uzskatÄ«ts par pÄrtrauktu, un tajÄ nav atļautas nekÄdas darbÄ«bas:
=> SELECT * FROM t;
ERROR: current transaction is aborted, commands ignored until end of transaction block
Un pat tad, ja mÄÄ£inÄt veikt izmaiÅas, PostgreSQL ziÅos par pÄrtraukÅ”anu:
=> COMMIT;
ROLLBACK
KÄpÄc darÄ«jums nevar turpinÄties pÄc neveiksmes? Fakts ir tÄds, ka kļūda varÄtu rasties tÄdÄ veidÄ, ka mÄs piekļūtu daļai izmaiÅu - tiktu pÄrkÄpts pat ne darÄ«juma atomitÄte, bet operators. TÄpat kÄ mÅ«su piemÄrÄ, kur operatoram izdevÄs atjauninÄt vienu rindiÅu pirms kļūdas:
=> 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)
JÄteic, ka psql ir režīms, kas joprojÄm ļauj darÄ«jumu turpinÄt pÄc neveiksmes, it kÄ kļūdainÄ operatora darbÄ«bas tiktu atsauktas.
=> 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;
Nav grÅ«ti uzminÄt, ka Å”ajÄ režīmÄ psql faktiski ievieto netieÅ”u saglabÄÅ”anas punktu pirms katras komandas un kļūmes gadÄ«jumÄ sÄk tÄs atcelÅ”anu. Å is režīms netiek izmantots pÄc noklusÄjuma, jo saglabÄÅ”anas punktu iestatÄ«Å”ana (pat neatgriežoties pie tiem) ir saistÄ«ta ar ievÄrojamÄm izmaksÄm.
Avots: www.habr.com