MVCC-3. የሕብረቁምፊ ስሪቶች

ስለዚህ, ተዛማጅ ጉዳዮችን ተመልክተናል የኢንሱሌሽን, እና ስለ ማፈግፈግ አድርጓል መረጃን በዝቅተኛ ደረጃ ማደራጀት. እና በመጨረሻ ወደ በጣም አስደሳች ክፍል ደርሰናል - የሕብረቁምፊ ስሪቶች።

ርዕስ

ቀደም ሲል እንደተናገርነው እያንዳንዱ ረድፍ በአንድ ጊዜ በበርካታ ስሪቶች ውስጥ በመረጃ ቋቱ ውስጥ ሊኖር ይችላል. አንዱ ስሪት በሆነ መንገድ ከሌላው መለየት አለበት ለዚህ አላማ እያንዳንዱ እትም የዚህን ስሪት "ጊዜ" የሚወስኑ ሁለት ምልክቶች አሉት (xmin እና xmax). በጥቅሶች ውስጥ - ምክንያቱም ጥቅም ላይ የሚውለው ጊዜ አይደለም, ነገር ግን ልዩ እየጨመረ የሚሄድ ቆጣሪ. እና ይህ ቆጣሪ የግብይት ቁጥሩ ነው።

(እንደተለመደው እውነታው የበለጠ የተወሳሰበ ነው፡ በቆጣሪው ውስን አቅም ምክንያት የግብይቱ ቁጥሩ ሁል ጊዜ ሊጨምር አይችልም። ግን ወደ በረዶነት ስንደርስ እነዚህን ዝርዝሮች በዝርዝር እንመለከታለን።)

ረድፍ ሲፈጠር xmin INSERT ትዕዛዝ ወደሰጠው የግብይት ቁጥር ተቀናብሯል እና xmax ባዶ ሆኖ ይቀራል።

አንድ ረድፍ ሲሰረዝ የአሁኑ ስሪት xmax እሴት ሰርዝ ባከናወነው የግብይት ቁጥር ምልክት ይደረግበታል።

አንድ ረድፍ በUPDATE ትዕዛዝ ሲቀየር፣ ሁለት ክዋኔዎች በትክክል ይከናወናሉ፡ ሰርዝ እና አስገባ። የአሁኑ የረድፍ እትም xmax ዝማኔውን ካከናወነው የግብይት ብዛት ጋር እኩል ያዘጋጃል። ከዚያም ተመሳሳይ ሕብረቁምፊ አዲስ ስሪት ተፈጥሯል; የእሱ xmin ዋጋ ከቀዳሚው ስሪት xmax ዋጋ ጋር ይዛመዳል።

የ xmin እና xmax መስኮች በረድፍ ሥሪት ራስጌ ውስጥ ተካትተዋል። ከእነዚህ መስኮች በተጨማሪ ራስጌው ሌሎችን ይዟል፣ ለምሳሌ፡-

  • infomask የዚህን ስሪት ባህሪያት የሚገልጹ ተከታታይ ቢትስ ነው. በጣም ብዙ ናቸው; ዋና ዋናዎቹን ቀስ በቀስ እንመለከታለን.
  • ctid ወደ ቀጣዩ አገናኝ አገናኝ ነው, ተመሳሳይ መሾመር አዲስ ስሪት. ለአዲሱ፣ አብዛኛው የአሁኑ የሕብረቁምፊ ስሪት፣ ctid የሚያመለክተው ይህን ስሪት ልሹ ነው። ቁጥሩ ቅጹ (x,y) አለው, x የገጽ ቁጥር ነው, y በድርድር ውስጥ ያለው የመረጃ ጠቋሚ ቁጥር ነው.
  • null bitmap - ባዶ እሴት (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)

የገጹን ይዘት እንይ። የገጽ ፍተሻ ቅጥያ ክምር_ገጽ_ንጥሎች ተግባር ስለ ጠቋሚዎች እና የረድፍ ስሪቶች መረጃ እንድታገኝ ይፈቅድልሃል፡

=> 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_የተሰረዙ ቢትስ የግብይት ቁጥር xmin መፈጸሙን (የተቋረጠ) መሆኑን ያመለክታሉ። ሁለት ተመሳሳይ ቢትስ የግብይት ቁጥር xmaxን ያመለክታሉ።

ስለምንታይ? አንድ ረድፍ በሚያስገቡበት ጊዜ ጠቋሚ ቁጥር 1 በሰንጠረዡ ገጽ ላይ ይታያል, ወደ የመጀመሪያው እና ብቸኛው የረድፉ ስሪት ይጠቁማል.

በሕብረቁምፊው ስሪት ውስጥ የ xmin መስኩ አሁን ባለው የግብይት ቁጥር ተሞልቷል። ግብይቱ አሁንም ንቁ ነው፣ ስለዚህ ሁለቱም የ xmin_committed እና xmin_aborted bits አልተዘጋጁም።

የረድፍ ስሪት 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 (commit log) ተብሎ ይጠራ ነበር እና ይህ ስም አሁንም በተለያዩ ቦታዎች ላይ ሊገኝ ይችላል).

XACT የስርዓት ካታሎግ ሰንጠረዥ አይደለም; እነዚህ በPGDATA/pg_xact ማውጫ ውስጥ ያሉ ፋይሎች ናቸው። ለእያንዳንዱ ግብይት ሁለት ቢት አላቸው፡ የተፈፀመ እና የተሰረዘ - ልክ እንደ በረድፍ ሥሪት ራስጌ። ይህ መረጃ ለመመቻቸት ብቻ በበርካታ ፋይሎች የተከፋፈለ ነው፡ ወደዚህ ጉዳይ መቀዝቀዝን ስናስብ ወደዚህ ጉዳይ እንመለሳለን። እና ከእነዚህ ፋይሎች ጋር መስራት ልክ እንደ ሌሎቹ ሁሉ ከገጽ በገጽ ይከናወናል።

ስለዚህ፣ በ XACT ውስጥ ግብይት ሲፈፀም፣ የተገባው ቢት ለዚህ ግብይት ተዘጋጅቷል። እና ይህ በሚፈፀምበት ጊዜ የሚከሰተው ይህ ብቻ ነው (ምንም እንኳን ስለ ቅድመ-ቀረጻ መዝገብ ገና እየተነጋገርን ባይሆንም)።

ሌላ ግብይት አሁን የተመለከትነውን የሰንጠረዥ ገጽ ሲደርስ፣ ብዙ ጥያቄዎችን መመለስ ይኖርበታል።

  1. የ xmin ግብይቱ ተጠናቅቋል? ካልሆነ, የተፈጠረው የሕብረቁምፊው ስሪት መታየት የለበትም.
    ይህ ቼክ የሚከናወነው በምሳሌው የጋራ ማህደረ ትውስታ ውስጥ የሚገኝ እና ፕሮካሬይ ተብሎ የሚጠራውን ሌላ መዋቅር በመመልከት ነው። እሱ የሁሉም ንቁ ሂደቶች ዝርዝር ይይዛል ፣ እና ለእያንዳንዱ የአሁኑ (ንቁ) ግብይቱ ቁጥር ይጠቁማል።
  2. ከተጠናቀቀ ታዲያ እንዴት - በመፈጸም ወይም በመሰረዝ? ከተሰረዘ የረድፉ ሥሪትም መታየት የለበትም።
    ይህ በትክክል XACT ነው. ነገር ግን፣ ምንም እንኳን የXACT የመጨረሻ ገፆች በ RAM ውስጥ ቋት ውስጥ ቢቀመጡም፣ XACTን በየጊዜው መፈተሽ አሁንም ውድ ነው። ስለዚህ፣ የግብይቱ ሁኔታ አንዴ ከተወሰነ፣ ወደ string version xmin_committed እና xmin_aborted bits ይጻፋል። ከነዚህ ቢትስ አንዱ ከተዋቀረ የ 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;

ማውጫዎች

እስካሁን ድረስ የተነጋገርነው ስለ ጠረጴዛ ገጾች ብቻ ነው. በመረጃ ጠቋሚዎች ውስጥ ምን ይከሰታል?

በመረጃ ጠቋሚ ገፆች ውስጥ ያለው መረጃ እንደየተወሰነው የመረጃ ጠቋሚ አይነት በእጅጉ ይለያያል። እና አንድ አይነት ኢንዴክስ እንኳን የተለያዩ አይነት ገጾች አሉት። ለምሳሌ፣ ቢ-ዛፍ የሜታዳታ ገጽ እና “መደበኛ” ገጾች አሉት።

ነገር ግን፣ ገጹ ብዙውን ጊዜ ወደ ረድፎች እና ረድፎቹ እራሳቸው (ልክ እንደ የጠረጴዛ ገጽ) ጠቋሚዎች አሉት። በተጨማሪም, በገጹ መጨረሻ ላይ ለየት ያለ ውሂብ የሚሆን ቦታ አለ.

በመረጃ ጠቋሚዎች ውስጥ ያሉ ረድፎች እንደ መረጃ ጠቋሚው ዓይነት በጣም የተለያዩ አወቃቀሮች ሊኖራቸው ይችላል። ለምሳሌ ለቢ-ዛፍ ከቅጠል ገፆች ጋር የተያያዙት ረድፎች የመረጃ ጠቋሚ ቁልፍ እሴት እና ማጣቀሻ (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 ይሰጣል. ቁጥሩ የሂደት መታወቂያ እና ተከታታይ ቁጥር ያካትታል.

ይህንን ቁጥር ማውጣት በሁሉም ሂደቶች መካከል ማመሳሰልን አይፈልግም እና ስለዚህ በጣም ፈጣን ነው. ስለ በረዶ ስናወራ ምናባዊ ቁጥሮችን የምንጠቀምበት ሌላ ምክንያት ጋር እንተዋወቃለን።

በመረጃ ቅጽበተ-ፎቶዎች ውስጥ ምናባዊ ቁጥሮች በምንም መልኩ ግምት ውስጥ አይገቡም።

በተለያዩ ጊዜያት በሲስተሙ ውስጥ ቀደም ሲል ጥቅም ላይ ከዋሉ ቁጥሮች ጋር ምናባዊ ግብይቶች ሊኖሩ ይችላሉ ፣ እና ይህ የተለመደ ነው። ነገር ግን እንዲህ ዓይነቱ ቁጥር በመረጃ ገፆች ውስጥ ሊፃፍ አይችልም, ምክንያቱም ገጹ በሚቀጥለው ጊዜ ሲደረስ ሁሉንም ትርጉሞች ሊያጣ ይችላል.

=> 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 ውስጥ ይገለጻል ነጥቦችን ያስቀምጡ (savepoint)፣ ይህም የአንድን ግብይት ሙሉ በሙሉ ሳያቋርጡ እንዲሰርዙ ያስችልዎታል። ነገር ግን ይህ ከላይ ካለው ሥዕላዊ መግለጫ ጋር አይጣጣምም ፣ ምክንያቱም ግብይቱ ለሁሉም ለውጦቹ ተመሳሳይ ሁኔታ ስላለው እና በአካል ምንም ውሂብ አይመለስም።

ይህንን ተግባር ተግባራዊ ለማድረግ፣ ከቁጠባ ነጥብ ጋር የሚደረግ ግብይት በተለያዩ ተከፍሏል። የተከማቸ ግብይቶች (ንዑስ ግብይት) ፣ የሁኔታው ሁኔታ በተናጥል ሊተዳደር ይችላል።

የጎጆ ግብይቶች የራሳቸው ቁጥር አላቸው (ከዋናው ግብይት ቁጥር ከፍ ያለ)። የጎጆ ግብይቶች ሁኔታ በ XACT ውስጥ በተለመደው መንገድ ተመዝግቧል, ነገር ግን የመጨረሻው ሁኔታ በዋናው ግብይት ሁኔታ ላይ የተመሰረተ ነው: ከተሰረዘ, ሁሉም የተዘጉ ግብይቶች እንዲሁ ይሰረዛሉ.

ስለ ግብይት መክተቻ መረጃ በPGDATA/pg_subtrans ማውጫ ውስጥ ባሉ ፋይሎች ውስጥ ተከማችቷል። ፋይሎች እንደ XACT ቋቶች በተመሳሳይ መልኩ በተደራጁ የምሳሌው የጋራ ማህደረ ትውስታ ውስጥ ባሉ ቋት በኩል ይደርሳሉ።

የጎጆ ግብይቶችን ከራስ ገዝ ግብይቶች ጋር አያምታቱ። በራስ ገዝ የሚደረጉ ግብይቶች በምንም መልኩ አንዳቸው በሌላው ላይ የተመኩ አይደሉም፣ ነገር ግን የጎጆ ግብይቶች ናቸው። በመደበኛ PostgreSQL ውስጥ የራስ ገዝ ግብይቶች የሉም ፣ እና ምናልባትም ፣ ለበጎ: በጣም በጣም አልፎ አልፎ ያስፈልጋሉ ፣ እና በሌሎች ዲቢኤምኤስ ውስጥ መገኘታቸው እንግልትን ያነሳሳል ፣ ከዚያ ሁሉም ሰው ይሰቃያል።

ሠንጠረዡን እናጽዳ፣ ግብይት እንጀምርና ረድፉን እናስገባ፡

=> 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 ከእያንዳንዱ ትዕዛዝ በፊት ስውር የማስቀመጫ ነጥብ እንደሚያስቀምጠው ለመገመት አስቸጋሪ አይደለም, እና ካልተሳካ ወደ እሱ መመለስን ይጀምራል. ይህ ሁነታ በነባሪነት ጥቅም ላይ አይውልም, ምክንያቱም የማስቀመጫ ነጥቦችን ማቀናበር (ወደ እነርሱ ሳይመለሱ እንኳን) ከፍተኛ ወጪን ያካትታል.

የቀጠለ።

ምንጭ: hab.com

አስተያየት ያክሉ