MVCC-3. د سټینګ نسخې

نو، موږ اړوند مسایل په پام کې نیولي دي موصلیت، او په اړه یې یو اعتکاف وکړ په ټیټه کچه د معلوماتو تنظیم کول. او په نهایت کې موږ خورا په زړه پوري برخې ته ورسیدو - د تار نسخې.

سرونکی

لکه څنګه چې موږ مخکې وویل، هر قطار کولی شي په ډیټابیس کې په څو نسخو کې په ورته وخت کې شتون ولري. یوه نسخه باید په یو ډول له بلې څخه توپیر ولري، د دې هدف لپاره، هره نسخه دوه نښې لري چې د دې نسخې د عمل "وخت" ټاکي (xmin او xmax). په نرخونو کې - ځکه چې دا هغه وخت نه دی لکه څنګه چې کارول کیږي ، مګر یو ځانګړی مخ په زیاتیدونکي کاونټر. او دا کاونټر د راکړې ورکړې شمیره ده.

(د معمول په څیر، حقیقت ډیر پیچلی دی: د لیږد شمیره د کاونټر د محدود ظرفیت له امله هر وخت نشي وده کولی. مګر موږ به دا توضیحات په تفصیل سره وګورو کله چې موږ کنګل کیدو ته ورسیږو.)

کله چې یو قطار جوړ شي، xmin د لیږد شمیرې ته ټاکل کیږي چې د INSERT کمانډ یې صادر کړی، او xmax خالي پاتې کیږي.

کله چې یو قطار ړنګ شي، د اوسني نسخې xmax ارزښت د لیږد شمیرې سره نښه کیږي چې DELETE یې ترسره کړی.

کله چې یو قطار د UPDATE کمانډ لخوا تعدیل شي، دوه عملیات په حقیقت کې ترسره کیږي: DELETE او INSERT. د قطار اوسنی نسخه 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)

راځئ چې د پاڼې محتويات وګورو. د پاڼې انسپیک توسیع د 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)

ورته، مګر د پام وړ لږ تفصيلي، معلومات پخپله د میز څخه ترلاسه کیدی شي، د pseudo-colums xmin او xmax په کارولو سره:

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

ټاکنه

که چیرې معامله په بریالیتوب سره بشپړه شي، تاسو اړتیا لرئ چې د هغې وضعیت په یاد ولرئ - په یاد ولرئ چې دا ژمن دی. د دې کولو لپاره، د XACT په نوم یو جوړښت کارول کیږي (او د 10 نسخه مخکې دا د CLOG (کمیټ log) په نوم یادیږي او دا نوم لاهم په مختلفو ځایونو کې موندل کیدی شي).

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 کې تعریف شوی ټکي خوندي کړئ (savepoint)، کوم چې تاسو ته اجازه درکوي د لیږد یوه برخه لغوه کړئ پرته له دې چې په بشپړ ډول مداخله وکړي. مګر دا په پورتني ډیاګرام کې مناسب نه دی، ځکه چې معامله د ټولو بدلونونو لپاره ورته حالت لري، او په فزیکي توګه هیڅ معلومات بیرته نه راځي.

د دې فعالیت پلي کولو لپاره، د خوندي نقطې سره معامله په څو جلا جلا ویشل شوې نیستې معاملې (فرعي لیږد)، د کوم حالت چې په جلا توګه اداره کیدی شي.

نیست شوي لیږدونه خپل شمیر لري (د اصلي لیږد له شمیر څخه لوړ). د نیست شوي لیږد حالت په معمول ډول په XACT کې ثبت شوی ، مګر وروستی حالت د اصلي لیږد حالت پورې اړه لري: که دا لغوه شي ، نو بیا ټول نیست شوي لیږدونه هم لغوه کیږي.

د لیږد نیټینګ په اړه معلومات د PGDATA/pg_subtrans لارښود کې په فایلونو کې زیرمه شوي. فایلونه د مثال په شریکه حافظه کې د بفرونو له لارې لاسرسی کیږي، د XACT بفرونو په څیر تنظیم شوي.

د نیست شوي لیږدونه د خپلواکو معاملو سره ګډوډ مه کوئ. خودمختاره راکړې ورکړې په هیڅ ډول په یو بل پورې اړه نلري، مګر د نیستې لیږدونه ترسره کوي. په منظم PostgreSQL کې هیڅ خپلواکه لیږد شتون نلري، او شاید، د غوره لپاره: دوی خورا لږ اړتیا لري، او په نورو DBMSs کې د دوی شتون ناوړه ګټه اخیستنه هڅوي، چې بیا هرڅوک ورسره مخ کیږي.

راځئ چې میز پاک کړو، معامله پیل کړو او قطار داخل کړو:

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

Add a comment