MVCC-3. స్ట్రింగ్ వెర్షన్లు

కాబట్టి, మేము సంబంధిత సమస్యలను పరిగణించాము ఇన్సులేషన్, మరియు గురించి తిరోగమనం చేసాడు తక్కువ స్థాయిలో డేటాను నిర్వహించడం. చివరకు మేము చాలా ఆసక్తికరమైన భాగానికి చేరుకున్నాము - స్ట్రింగ్ వెర్షన్లు.

శీర్షిక

మేము ఇప్పటికే చెప్పినట్లుగా, ప్రతి అడ్డు వరుస ఏకకాలంలో డేటాబేస్లో అనేక సంస్కరణల్లో ఉంటుంది. ఒక సంస్కరణ తప్పనిసరిగా మరొక దాని నుండి వేరు చేయబడాలి. ఈ ప్రయోజనం కోసం, ప్రతి సంస్కరణకు ఈ సంస్కరణ (xmin మరియు xmax) చర్య యొక్క "సమయం" నిర్ణయించే రెండు మార్కులు ఉంటాయి. కోట్స్‌లో - ఎందుకంటే ఇది ఉపయోగించబడే సమయం కాదు, ప్రత్యేక పెరుగుతున్న కౌంటర్. మరియు ఈ కౌంటర్ లావాదేవీ సంఖ్య.

(ఎప్పటిలాగే, వాస్తవికత మరింత క్లిష్టంగా ఉంటుంది: కౌంటర్ యొక్క పరిమిత బిట్ సామర్థ్యం కారణంగా లావాదేవీ సంఖ్య అన్ని సమయాలలో పెరగదు. కానీ మేము ఫ్రీజింగ్‌కి వచ్చినప్పుడు ఈ వివరాలను వివరంగా పరిశీలిస్తాము.)

అడ్డు వరుస సృష్టించబడినప్పుడు, INSERT ఆదేశాన్ని జారీ చేసిన లావాదేవీ సంఖ్యకు xmin సెట్ చేయబడుతుంది మరియు xmax ఖాళీగా ఉంచబడుతుంది.

అడ్డు వరుస తొలగించబడినప్పుడు, ప్రస్తుత సంస్కరణ యొక్క xmax విలువ, DELETE చేసిన లావాదేవీ సంఖ్యతో గుర్తించబడుతుంది.

UPDATE కమాండ్ ద్వారా అడ్డు వరుస సవరించబడినప్పుడు, వాస్తవానికి రెండు కార్యకలాపాలు నిర్వహించబడతాయి: DELETE మరియు INSERT. అడ్డు వరుస యొక్క ప్రస్తుత సంస్కరణ UPDATE చేసిన లావాదేవీ సంఖ్యకు సమానంగా xmaxని సెట్ చేస్తుంది. అదే స్ట్రింగ్ యొక్క కొత్త వెర్షన్ అప్పుడు సృష్టించబడుతుంది; దాని xmin విలువ మునుపటి సంస్కరణ యొక్క xmax విలువతో సమానంగా ఉంటుంది.

xmin మరియు xmax ఫీల్డ్‌లు అడ్డు వరుస వెర్షన్ హెడర్‌లో చేర్చబడ్డాయి. ఈ ఫీల్డ్‌లతో పాటు, హెడర్‌లో ఇతరాలు ఉన్నాయి, ఉదాహరణకు:

  • infomask అనేది ఈ సంస్కరణ యొక్క లక్షణాలను నిర్వచించే బిట్‌ల శ్రేణి. వాటిలో చాలా ఉన్నాయి; మేము క్రమంగా ప్రధాన వాటిని పరిశీలిస్తాము.
  • ctid అదే లైన్ యొక్క తదుపరి, కొత్త వెర్షన్‌కి లింక్. లైన్ యొక్క సరికొత్త, అత్యంత ప్రస్తుత వెర్షన్ కోసం, ctid ఈ సంస్కరణను సూచిస్తుంది. సంఖ్యకు ఫారమ్ (x,y) ఉంటుంది, ఇక్కడ x అనేది పేజీ సంఖ్య, y అనేది శ్రేణిలోని సూచిక సంఖ్య.
  • శూన్య బిట్‌మ్యాప్ - శూన్య విలువ (NULL)ని కలిగి ఉన్న ఇచ్చిన సంస్కరణ యొక్క నిలువు వరుసలను గుర్తు చేస్తుంది. NULL అనేది సాధారణ డేటా రకం విలువలలో ఒకటి కాదు, కాబట్టి లక్షణాన్ని విడిగా నిల్వ చేయాలి.

ఫలితంగా, హెడర్ చాలా పెద్దది - లైన్ యొక్క ప్రతి సంస్కరణకు కనీసం 23 బైట్‌లు మరియు సాధారణంగా NULL బిట్‌మ్యాప్ కారణంగా ఎక్కువ. పట్టిక "ఇరుకైనది" (అంటే, కొన్ని నిలువు వరుసలను కలిగి ఉంటే), ఓవర్‌హెడ్ ఉపయోగకరమైన సమాచారం కంటే ఎక్కువ తీసుకోవచ్చు.

ఇన్సర్ట్

చొప్పించడంతో ప్రారంభించి తక్కువ-స్థాయి స్ట్రింగ్ ఆపరేషన్లు ఎలా నిర్వహించబడతాయో నిశితంగా పరిశీలిద్దాం.

ప్రయోగాల కోసం, రెండు నిలువు వరుసలు మరియు వాటిలో ఒకదానిపై సూచికతో కొత్త పట్టికను సృష్టిద్దాం:

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

లావాదేవీని ప్రారంభించిన తర్వాత ఒక అడ్డు వరుసను చొప్పించండి.

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

మా ప్రస్తుత లావాదేవీ సంఖ్య ఇక్కడ ఉంది:

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

పేజీలోని విషయాలను చూద్దాం. pageinspect పొడిగింపు యొక్క heap_page_items ఫంక్షన్ పాయింటర్‌లు మరియు అడ్డు వరుస సంస్కరణల గురించి సమాచారాన్ని పొందడానికి మిమ్మల్ని అనుమతిస్తుంది:

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

PostgreSQLలోని హీప్ అనే పదం పట్టికలను సూచిస్తుందని గమనించండి. ఈ పదం యొక్క మరొక వింత ఉపయోగం - ఒక కుప్ప అంటారు డేటా నిర్మాణం, ఇది పట్టికతో ఉమ్మడిగా ఏమీ లేదు. ఇక్కడ పదం ఆర్డర్ చేయబడిన సూచికలకు విరుద్ధంగా "ప్రతిదీ కలిసి విసిరివేయబడింది" అనే అర్థంలో ఉపయోగించబడింది.

ఫంక్షన్ డేటాను "ఉన్నట్లుగా" చూపుతుంది, అర్థం చేసుకోవడం కష్టం. దాన్ని గుర్తించడానికి, మేము సమాచారంలో కొంత భాగాన్ని మాత్రమే వదిలివేస్తాము మరియు దానిని అర్థంచేసుకుంటాము:

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

మేము చేసినది ఇక్కడ ఉంది:

  • t_ctid: (పేజీ సంఖ్య, సూచిక సంఖ్య) వలె కనిపించేలా చేయడానికి సూచిక సంఖ్యకు సున్నా జోడించబడింది.
  • lp_flags పాయింటర్ స్థితిని అర్థంచేసుకుంది. ఇక్కడ ఇది "సాధారణం" - దీని అర్థం పాయింటర్ వాస్తవానికి స్ట్రింగ్ యొక్క సంస్కరణను సూచిస్తుంది. మేము ఇతర అర్థాలను తరువాత చూద్దాం.
  • అన్ని సమాచార బిట్‌లలో, ఇప్పటివరకు రెండు జతలను మాత్రమే గుర్తించారు. xmin_committed మరియు xmin_aborted బిట్‌లు లావాదేవీ సంఖ్య xmin కట్టుబడి ఉందో లేదో సూచిస్తాయి (నిలిపివేయబడింది). రెండు సారూప్య బిట్‌లు లావాదేవీ సంఖ్య xmaxని సూచిస్తాయి.

మనం ఏమి చూస్తాము? మీరు అడ్డు వరుసను చొప్పించినప్పుడు, పట్టిక పేజీలో సూచిక సంఖ్య 1 కనిపిస్తుంది, ఇది అడ్డు వరుస యొక్క మొదటి మరియు ఏకైక సంస్కరణను సూచిస్తుంది.

స్ట్రింగ్ వెర్షన్‌లో, xmin ఫీల్డ్ ప్రస్తుత లావాదేవీ సంఖ్యతో నిండి ఉంటుంది. లావాదేవీ ఇప్పటికీ సక్రియంగా ఉంది, కాబట్టి xmin_committed మరియు xmin_aborted బిట్‌లు రెండూ సెట్ చేయబడలేదు.

అడ్డు వరుస సంస్కరణ ctid ఫీల్డ్ అదే అడ్డు వరుసను సూచిస్తుంది. కొత్త వెర్షన్ ఉనికిలో లేదని దీని అర్థం.

xmax ఫీల్డ్ డమ్మీ నంబర్ 0తో నిండి ఉంది ఎందుకంటే అడ్డు వరుస యొక్క ఈ సంస్కరణ తొలగించబడలేదు మరియు ప్రస్తుతము. xmax_aborted బిట్ సెట్ చేయబడినందున లావాదేవీలు ఈ నంబర్‌పై దృష్టి పెట్టవు.

లావాదేవీ నంబర్‌లకు సమాచార బిట్‌లను జోడించడం ద్వారా చదవగలిగే సామర్థ్యాన్ని మెరుగుపరచడానికి మరో అడుగు వేద్దాం. మరియు ఒక ఫంక్షన్‌ని క్రియేట్ చేద్దాం, ఎందుకంటే మనకు ఒకటి కంటే ఎక్కువసార్లు అభ్యర్థన అవసరం అవుతుంది:

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

ఈ రూపంలో, అడ్డు వరుస సంస్కరణ యొక్క హెడర్‌లో ఏమి జరుగుతుందో చాలా స్పష్టంగా ఉంటుంది:

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

xmin మరియు xmax అనే నకిలీ నిలువు వరుసలను ఉపయోగించి, సారూప్యమైన, కానీ గణనీయంగా తక్కువ వివరంగా, సమాచారాన్ని పట్టిక నుండే పొందవచ్చు:

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

ఫిక్సేషన్

లావాదేవీ విజయవంతంగా పూర్తయినట్లయితే, మీరు దాని స్థితిని గుర్తుంచుకోవాలి - ఇది కట్టుబడి ఉందని గమనించండి. దీన్ని చేయడానికి, XACT అనే నిర్మాణం ఉపయోగించబడుతుంది (మరియు వెర్షన్ 10 కి ముందు దీనిని CLOG (కమిట్ లాగ్) అని పిలిచేవారు మరియు ఈ పేరు ఇప్పటికీ వివిధ ప్రదేశాలలో కనుగొనబడుతుంది).

XACT అనేది సిస్టమ్ కేటలాగ్ పట్టిక కాదు; ఇవి PGDATA/pg_xact డైరెక్టరీలోని ఫైల్‌లు. అవి ప్రతి లావాదేవీకి రెండు బిట్‌లను కలిగి ఉంటాయి: కట్టుబడి మరియు రద్దు చేయబడ్డాయి - వరుస వెర్షన్ హెడర్‌లో వలె. ఈ సమాచారం సౌలభ్యం కోసం మాత్రమే అనేక ఫైల్‌లుగా విభజించబడింది; మేము గడ్డకట్టడాన్ని పరిగణించినప్పుడు మేము ఈ సమస్యకు తిరిగి వస్తాము. మరియు ఈ ఫైల్‌లతో పని అన్ని ఇతర వాటిలాగే పేజీలవారీగా నిర్వహించబడుతుంది.

కాబట్టి, XACTలో లావాదేవీ జరిగినప్పుడు, ఈ లావాదేవీకి కట్టుబడి ఉన్న బిట్ సెట్ చేయబడుతుంది. మరియు ఇది కమిట్ చేసే సమయంలో జరిగేది (మేము ఇంకా ప్రీ-రికార్డింగ్ లాగ్ గురించి మాట్లాడటం లేదు).

మరొక లావాదేవీ మేము ఇప్పుడే చూసిన టేబుల్ పేజీని యాక్సెస్ చేసినప్పుడు, అది అనేక ప్రశ్నలకు సమాధానం ఇవ్వవలసి ఉంటుంది.

  1. xmin లావాదేవీ పూర్తయిందా? కాకపోతే, స్ట్రింగ్ యొక్క సృష్టించబడిన సంస్కరణ కనిపించకూడదు.
    ఈ చెక్ మరొక నిర్మాణాన్ని చూడటం ద్వారా నిర్వహించబడుతుంది, ఇది ఉదాహరణ యొక్క షేర్డ్ మెమరీలో ఉంది మరియు దీనిని ProcArray అంటారు. ఇది అన్ని క్రియాశీల ప్రక్రియల జాబితాను కలిగి ఉంటుంది మరియు ప్రతి దాని ప్రస్తుత (క్రియాశీల) లావాదేవీ సంఖ్య సూచించబడుతుంది.
  2. పూర్తయినట్లయితే, ఎలా - కట్టుబడి లేదా రద్దు చేయడం ద్వారా? రద్దు చేయబడితే, అడ్డు వరుస వెర్షన్ కూడా కనిపించకూడదు.
    XACT అంటే సరిగ్గా ఇదే. కానీ, XACT యొక్క చివరి పేజీలు RAMలోని బఫర్‌లలో నిల్వ చేయబడినప్పటికీ, ప్రతిసారీ XACTని తనిఖీ చేయడం ఇప్పటికీ ఖరీదైనది. కాబట్టి, లావాదేవీ స్థితిని నిర్ణయించిన తర్వాత, అది స్ట్రింగ్ వెర్షన్ యొక్క xmin_committed మరియు xmin_aborted బిట్‌లకు వ్రాయబడుతుంది. ఈ బిట్‌లలో ఒకటి సెట్ చేయబడితే, లావాదేవీ xmin స్థితి తెలిసినట్లుగా పరిగణించబడుతుంది మరియు తదుపరి లావాదేవీకి XACTని యాక్సెస్ చేయవలసిన అవసరం లేదు.

ఈ బిట్‌లు ఇన్సర్ట్ చేయడం ద్వారా లావాదేవీ ద్వారా ఎందుకు సెట్ చేయబడవు? ఇన్సర్ట్ జరిగినప్పుడు, లావాదేవీ విజయవంతమవుతుందో లేదో ఇంకా తెలియదు. మరియు కట్టుబడి ఉన్న సమయంలో, ఏ పేజీలలో ఏ పంక్తులు మార్చబడ్డాయి అనేది ఇకపై స్పష్టంగా లేదు. అటువంటి పేజీలు చాలా ఉండవచ్చు మరియు వాటిని గుర్తుంచుకోవడం లాభదాయకం కాదు. అదనంగా, కొన్ని పేజీలు బఫర్ కాష్ నుండి డిస్క్‌కు తొలగించబడతాయి; బిట్‌లను మార్చడానికి వాటిని మళ్లీ చదవడం వలన నిబద్ధత గణనీయంగా తగ్గుతుంది.

పొదుపు యొక్క ప్రతికూలత ఏమిటంటే, మార్పుల తర్వాత, ఏదైనా లావాదేవీ (ఒక సాధారణ రీడ్ - SELECT నిర్వహించడం కూడా) బఫర్ కాష్‌లోని డేటా పేజీలను మార్చడం ప్రారంభించవచ్చు.

కాబట్టి, మార్పును సరిచేద్దాం.

=> COMMIT;

పేజీలో ఏదీ మారలేదు (కానీ లావాదేవీ స్థితి ఇప్పటికే XACTలో రికార్డ్ చేయబడిందని మాకు తెలుసు):

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

ఇప్పుడు మొదట పేజీని యాక్సెస్ చేసే లావాదేవీ xmin లావాదేవీ స్థితిని గుర్తించి, సమాచార బిట్‌లకు వ్రాయాలి:

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

తొలగింపు

అడ్డు వరుస తొలగించబడినప్పుడు, ప్రస్తుత తొలగింపు లావాదేవీ సంఖ్య ప్రస్తుత సంస్కరణ యొక్క xmax ఫీల్డ్‌కు వ్రాయబడుతుంది మరియు xmax_aborted బిట్ క్లియర్ చేయబడుతుంది.

క్రియాశీల లావాదేవీకి సంబంధించిన xmax సెట్ విలువ అడ్డు వరుస లాక్‌గా పనిచేస్తుందని గమనించండి. మరొక లావాదేవీ ఈ అడ్డు వరుసను నవీకరించాలనుకుంటే లేదా తొలగించాలనుకుంటే, లావాదేవీ xmax పూర్తయ్యే వరకు వేచి ఉండవలసి వస్తుంది. మేము తరువాత నిరోధించడం గురించి మరింత మాట్లాడుతాము. ప్రస్తుతానికి, అడ్డు వరుసల తాళాల సంఖ్య అపరిమితంగా ఉందని మేము గమనించాము. వారు RAMలో స్థలాన్ని తీసుకోరు మరియు సిస్టమ్ పనితీరు వారి సంఖ్యతో బాధపడదు. నిజమే, "సుదీర్ఘ" లావాదేవీలు ఇతర ప్రతికూలతలను కలిగి ఉంటాయి, కానీ తర్వాత మరింత.

లైన్ డిలీట్ చేద్దాం.

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

లావాదేవీ సంఖ్య xmax ఫీల్డ్‌లో వ్రాయబడిందని మేము చూస్తాము, కానీ సమాచార బిట్‌లు సెట్ చేయబడలేదు:

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

రద్దు

మార్పులను నిలిపివేయడం అనేది కట్టుబడి ఉన్నట్లుగానే పని చేస్తుంది, కేవలం XACTలో మాత్రమే లావాదేవీకి నిలిపివేయబడిన బిట్ సెట్ చేయబడుతుంది. చర్య రద్దు చేయడం ఎంత వేగంగా జరుగుతుంది. కమాండ్‌ను ROLLBACK అని పిలిచినప్పటికీ, మార్పులు వెనక్కి తీసుకోబడవు: డేటా పేజీలలో లావాదేవీని మార్చగలిగే ప్రతిదీ మారదు.

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

పేజీని యాక్సెస్ చేసినప్పుడు, స్థితి తనిఖీ చేయబడుతుంది మరియు xmax_aborted సూచన బిట్ అడ్డు వరుస సంస్కరణకు సెట్ చేయబడుతుంది. xmax సంఖ్య పేజీలోనే ఉంటుంది, కానీ ఎవరూ దానిని చూడరు.

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

నవీకరణ

అప్‌డేట్ మొదట అడ్డు వరుస యొక్క ప్రస్తుత సంస్కరణను తొలగించి, ఆపై కొత్తదాన్ని చొప్పించినట్లుగా పనిచేస్తుంది.

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

ప్రశ్న ఒక లైన్‌ను ఉత్పత్తి చేస్తుంది (కొత్త వెర్షన్):

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

కానీ పేజీలో మేము రెండు వెర్షన్లను చూస్తాము:

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

తొలగించబడిన సంస్కరణ xmax ఫీల్డ్‌లో ప్రస్తుత లావాదేవీ సంఖ్యతో గుర్తించబడింది. అంతేకాకుండా, మునుపటి లావాదేవీ రద్దు చేయబడినందున, ఈ విలువ పాతదానిపై వ్రాయబడింది. మరియు ప్రస్తుత లావాదేవీ యొక్క స్థితి ఇంకా తెలియనందున xmax_aborted బిట్ క్లియర్ చేయబడింది.

లైన్ యొక్క మొదటి సంస్కరణ ఇప్పుడు రెండవ (t_ctid ఫీల్డ్)ని కొత్తదిగా సూచిస్తుంది.

రెండవ సూచిక సూచిక పేజీలో కనిపిస్తుంది మరియు రెండవ వరుస పట్టిక పేజీలో రెండవ సంస్కరణను సూచిస్తుంది.

తొలగింపు మాదిరిగానే, అడ్డు వరుస యొక్క మొదటి సంస్కరణలో xmax విలువ అడ్డు వరుస లాక్ చేయబడిందని సూచిస్తుంది.

సరే, లావాదేవీని పూర్తి చేద్దాం.

=> COMMIT;

సూచికలు

ఇప్పటివరకు మనం టేబుల్ పేజీల గురించి మాత్రమే మాట్లాడుకున్నాము. ఇండెక్స్ లోపల ఏమి జరుగుతుంది?

ఇండెక్స్ పేజీలలోని సమాచారం నిర్దిష్ట రకం ఇండెక్స్‌పై ఆధారపడి చాలా తేడా ఉంటుంది. మరియు ఒక రకమైన సూచిక కూడా వివిధ రకాల పేజీలను కలిగి ఉంటుంది. ఉదాహరణకు, B-ట్రీలో మెటాడేటా పేజీ మరియు “రెగ్యులర్” పేజీలు ఉంటాయి.

అయితే, పేజీ సాధారణంగా వరుసలకు మరియు అడ్డు వరుసలకు (టేబుల్ పేజీ వలె) పాయింటర్‌ల శ్రేణిని కలిగి ఉంటుంది. అదనంగా, పేజీ చివరిలో ప్రత్యేక డేటా కోసం స్థలం ఉంది.

ఇండెక్స్‌లోని వరుసలు కూడా ఇండెక్స్ రకాన్ని బట్టి చాలా భిన్నమైన నిర్మాణాలను కలిగి ఉంటాయి. ఉదాహరణకు, B-ట్రీ కోసం, లీఫ్ పేజీలకు సంబంధించిన అడ్డు వరుసలు ఇండెక్సింగ్ కీ విలువను మరియు సంబంధిత పట్టిక వరుసకు సూచన (ctid)ని కలిగి ఉంటాయి. సాధారణంగా, ఇండెక్స్ పూర్తిగా భిన్నమైన రీతిలో నిర్మించబడవచ్చు.

అతి ముఖ్యమైన విషయం ఏమిటంటే, ఏ రకమైన సూచికలలో వరుస సంస్కరణలు లేవు. బాగా, లేదా ప్రతి పంక్తి ఖచ్చితంగా ఒక సంస్కరణ ద్వారా సూచించబడుతుందని మేము ఊహించవచ్చు. మరో మాటలో చెప్పాలంటే, ఇండెక్స్ అడ్డు వరుస హెడర్‌లో xmin మరియు xmax ఫీల్డ్‌లు లేవు. సూచిక నుండి లింక్‌లు అడ్డు వరుసల యొక్క అన్ని పట్టిక సంస్కరణలకు దారితీస్తాయని మేము భావించవచ్చు - కాబట్టి మీరు పట్టికను చూడటం ద్వారా లావాదేవీ ఏ సంస్కరణను చూస్తారో గుర్తించవచ్చు. (ఎప్పటిలాగే, ఇది పూర్తి నిజం కాదు. కొన్ని సందర్భాల్లో, విజిబిలిటీ మ్యాప్ ప్రక్రియను ఆప్టిమైజ్ చేయగలదు, కానీ మేము దీనిని మరింత వివరంగా తరువాత పరిశీలిస్తాము.)

అదే సమయంలో, ఇండెక్స్ పేజీలో మేము ప్రస్తుత మరియు పాత రెండు వెర్షన్‌లకు పాయింటర్‌లను కనుగొంటాము:

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

వర్చువల్ లావాదేవీలు

ఆచరణలో, PostgreSQL లావాదేవీ సంఖ్యలను "సేవ్" చేయడానికి అనుమతించే ఆప్టిమైజేషన్లను ఉపయోగిస్తుంది.

లావాదేవీ డేటాను మాత్రమే చదివితే, అది అడ్డు వరుస సంస్కరణల దృశ్యమానతపై ప్రభావం చూపదు. అందువల్ల, సేవా ప్రక్రియ మొదట లావాదేవీకి వర్చువల్ xidని జారీ చేస్తుంది. నంబర్ ప్రాసెస్ ID మరియు సీక్వెన్స్ నంబర్‌ను కలిగి ఉంటుంది.

ఈ సంఖ్యను జారీ చేయడానికి అన్ని ప్రక్రియల మధ్య సమకాలీకరణ అవసరం లేదు మరియు అందువల్ల చాలా వేగంగా ఉంటుంది. మేము గడ్డకట్టడం గురించి మాట్లాడేటప్పుడు వర్చువల్ నంబర్లను ఉపయోగించడం కోసం మరొక కారణంతో మేము పరిచయం చేస్తాము.

డేటా స్నాప్‌షాట్‌లలో వర్చువల్ నంబర్‌లు ఏ విధంగానూ పరిగణనలోకి తీసుకోబడవు.

వేర్వేరు సమయాల్లో, సిస్టమ్‌లో ఇప్పటికే ఉపయోగించిన సంఖ్యలతో వర్చువల్ లావాదేవీలు ఉండవచ్చు మరియు ఇది సాధారణం. కానీ అటువంటి సంఖ్య డేటా పేజీలలోకి వ్రాయబడదు, ఎందుకంటే తదుపరిసారి పేజీని యాక్సెస్ చేసినప్పుడు అది మొత్తం అర్థాన్ని కోల్పోవచ్చు.

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

లావాదేవీ డేటాను మార్చడం ప్రారంభిస్తే, దానికి నిజమైన, ప్రత్యేకమైన లావాదేవీ సంఖ్య ఇవ్వబడుతుంది.

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

=> COMMIT;

నెస్టెడ్ లావాదేవీలు

పాయింట్లను సేవ్ చేయండి

SQLలో నిర్వచించబడింది పాయింట్లను సేవ్ చేయండి (సేవ్ పాయింట్), ఇది లావాదేవీలో కొంత భాగాన్ని పూర్తిగా అంతరాయం కలిగించకుండా రద్దు చేయడానికి మిమ్మల్ని అనుమతిస్తుంది. కానీ ఇది పై రేఖాచిత్రానికి సరిపోదు, ఎందుకంటే లావాదేవీ దాని అన్ని మార్పులకు ఒకే స్థితిని కలిగి ఉంటుంది మరియు భౌతికంగా డేటా ఏదీ వెనక్కి తీసుకోబడదు.

ఈ ఫంక్షనాలిటీని అమలు చేయడానికి, సేవ్‌పాయింట్‌తో లావాదేవీ అనేక విడివిడిగా విభజించబడింది సమూహ లావాదేవీలు (ఉపవివర్తన), దీని స్థితిని విడిగా నిర్వహించవచ్చు.

నెస్టెడ్ లావాదేవీలు వాటి స్వంత సంఖ్యను కలిగి ఉంటాయి (ప్రధాన లావాదేవీ సంఖ్య కంటే ఎక్కువ). సమూహ లావాదేవీల స్థితి XACTలో సాధారణ పద్ధతిలో నమోదు చేయబడుతుంది, అయితే తుది స్థితి ప్రధాన లావాదేవీ యొక్క స్థితిపై ఆధారపడి ఉంటుంది: ఇది రద్దు చేయబడితే, అన్ని సమూహ లావాదేవీలు కూడా రద్దు చేయబడతాయి.

లావాదేవీ గూడు గురించిన సమాచారం PGDATA/pg_subtrans డైరెక్టరీలోని ఫైల్‌లలో నిల్వ చేయబడుతుంది. XACT బఫర్‌ల మాదిరిగానే నిర్వహించబడిన ఉదాహరణ యొక్క షేర్డ్ మెమరీలోని బఫర్‌ల ద్వారా ఫైల్‌లు యాక్సెస్ చేయబడతాయి.

స్వయంప్రతిపత్త లావాదేవీలతో సమూహ లావాదేవీలను కంగారు పెట్టవద్దు. స్వయంప్రతిపత్త లావాదేవీలు ఏ విధంగానూ ఒకదానిపై ఒకటి ఆధారపడవు, కానీ సమూహ లావాదేవీలు చేస్తాయి. సాధారణ PostgreSQLలో స్వయంప్రతిపత్త లావాదేవీలు లేవు, మరియు, బహుశా, ఉత్తమమైన వాటి కోసం: అవి చాలా అరుదుగా అవసరమవుతాయి మరియు ఇతర DBMSలలో వారి ఉనికి దుర్వినియోగాన్ని రేకెత్తిస్తుంది, దాని నుండి ప్రతి ఒక్కరూ బాధపడతారు.

పట్టికను క్లియర్ చేసి, లావాదేవీని ప్రారంభించి, అడ్డు వరుసను చొప్పించండి:

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

ఇప్పుడు సేవ్ పాయింట్‌ని ఉంచి, మరొక పంక్తిని ఇన్‌సర్ట్ చేద్దాం.

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

txid_current() ఫంక్షన్ ప్రధాన లావాదేవీ సంఖ్యను అందిస్తుంది, సమూహ లావాదేవీ సంఖ్య కాదు.

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

సేవ్ పాయింట్‌కి తిరిగి వెళ్లి మూడవ పంక్తిని ఇన్సర్ట్ చేద్దాం.

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

పేజీలో మేము రద్దు చేయబడిన సమూహ లావాదేవీ ద్వారా జోడించబడిన అడ్డు వరుసను చూడటం కొనసాగిస్తాము.

మేము మార్పులను సరిచేస్తాము.

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

ప్రతి సమూహ లావాదేవీకి దాని స్వంత స్థితి ఉందని ఇప్పుడు మీరు స్పష్టంగా చూడవచ్చు.

SQLలో సమీకృత లావాదేవీలు స్పష్టంగా ఉపయోగించబడవని గుర్తుంచుకోండి, అంటే, మీరు ప్రస్తుత లావాదేవీని పూర్తి చేయకుండా కొత్త లావాదేవీని ప్రారంభించలేరు. సేవ్‌పాయింట్‌లను ఉపయోగిస్తున్నప్పుడు, అలాగే PL/pgSQL మినహాయింపులను నిర్వహించేటప్పుడు మరియు అనేక ఇతర, మరింత అన్యదేశ సందర్భాలలో ఈ మెకానిజం పరోక్షంగా సక్రియం చేయబడుతుంది.

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

ఆపరేషన్ల లోపాలు మరియు అటామిసిటీ

ఆపరేషన్ చేస్తున్నప్పుడు లోపం సంభవించినట్లయితే ఏమి జరుగుతుంది? ఉదాహరణకు, ఇలా:

=> 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;
ERROR:  current transaction is aborted, commands ignored until end of transaction block

మరియు మీరు మార్పులను చేయడానికి ప్రయత్నించినప్పటికీ, PostgreSQL రద్దును నివేదిస్తుంది:

=> COMMIT;
ROLLBACK

విఫలమైన తర్వాత లావాదేవీని ఎందుకు కొనసాగించలేరు? వాస్తవం ఏమిటంటే, మార్పులలో కొంత భాగాన్ని మనం యాక్సెస్ చేసే విధంగా లోపం తలెత్తవచ్చు - లావాదేవీ యొక్క పరమాణుత్వం కూడా కాదు, కానీ ఆపరేటర్ ఉల్లంఘించబడుతుంది. మా ఉదాహరణలో వలె, ఆపరేటర్ దోషానికి ముందు ఒక పంక్తిని అప్‌డేట్ చేయగలిగారు:

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

psql ఒక మోడ్‌ను కలిగి ఉందని చెప్పాలి, అది విఫలమైన తర్వాత కూడా లావాదేవీని తప్పుగా ఆపరేటర్ యొక్క చర్యలు వెనక్కి తీసుకున్నట్లుగా కొనసాగించడానికి అనుమతిస్తుంది.

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

ఈ మోడ్‌లో, psql వాస్తవానికి ప్రతి కమాండ్‌కు ముందు ఒక అవ్యక్తమైన సేవ్ పాయింట్‌ను ఉంచుతుందని ఊహించడం కష్టం కాదు, మరియు వైఫల్యం సంభవించినప్పుడు దానికి రోల్‌బ్యాక్‌ను ప్రారంభిస్తుంది. ఈ మోడ్ డిఫాల్ట్‌గా ఉపయోగించబడదు, ఎందుకంటే సేవ్ పాయింట్‌లను సెట్ చేయడం (వాటికి తిరిగి వెళ్లకుండా కూడా) గణనీయమైన ఓవర్‌హెడ్‌ను కలిగి ఉంటుంది.

కొనసాగించాలి.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి