MVCC-3. ВСрсии строк

Π˜Ρ‚Π°ΠΊ, ΠΌΡ‹ рассмотрСли вопросы, связанныС с изоляциСй, ΠΈ сдСлали отступлСниС ΠΎΠ± ΠΎΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Ρ… Π½Π° Π½ΠΈΠ·ΠΊΠΎΠΌ ΡƒΡ€ΠΎΠ²Π½Π΅. И Π½Π°ΠΊΠΎΠ½Π΅Ρ† Π΄ΠΎΠ±Ρ€Π°Π»ΠΈΡΡŒ Π΄ΠΎ самого интСрСсного β€” Π΄ΠΎ вСрсий строк.

Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ

Как ΠΌΡ‹ ΡƒΠΆΠ΅ Π³ΠΎΠ²ΠΎΡ€ΠΈΠ»ΠΈ, каТдая строка ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ ΠΏΡ€ΠΈΡΡƒΡ‚ΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ Π² Π±Π°Π·Π΅ Π΄Π°Π½Π½Ρ‹Ρ… Π² Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… вСрсиях. ΠžΠ΄Π½Ρƒ Π²Π΅Ρ€ΡΠΈΡŽ ΠΎΡ‚ Π΄Ρ€ΡƒΠ³ΠΎΠΉ Π½Π°Π΄ΠΎ ΠΊΠ°ΠΊ-Ρ‚ΠΎ ΠΎΡ‚Π»ΠΈΡ‡Π°Ρ‚ΡŒ Π‘ этой Ρ†Π΅Π»ΡŒΡŽ каТдая вСрсия ΠΈΠΌΠ΅Π΅Ρ‚ Π΄Π²Π΅ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΊΠΈ, ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‰ΠΈΠ΅ «врСмя» дСйствия Π΄Π°Π½Π½ΠΎΠΉ вСрсии (xmin ΠΈ xmax). Π’ ΠΊΠ°Π²Ρ‹Ρ‡ΠΊΠ°Ρ… β€” ΠΏΠΎΡ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π½Π΅ врСмя ΠΊΠ°ΠΊ Ρ‚Π°ΠΊΠΎΠ²ΠΎΠ΅, Π° ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉΡΡ счСтчик. И этот счСтчик β€” Π½ΠΎΠΌΠ΅Ρ€ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ.

(Как ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ, Π½Π° самом Π΄Π΅Π»Π΅ всС слоТнСС: Π½ΠΎΠΌΠ΅Ρ€ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΉ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ всС врСмя ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Ρ‚ΡŒΡΡ ΠΈΠ·-Π·Π° ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½ΠΎΠΉ разрядности счСтчика. Но эти Π΄Π΅Ρ‚Π°Π»ΠΈ ΠΌΡ‹ рассмотрим ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ, ΠΊΠΎΠ³Π΄Π° Π΄ΠΎΠΉΠ΄Π΅ΠΌ Π΄ΠΎ Π·Π°ΠΌΠΎΡ€ΠΎΠ·ΠΊΠΈ.)

Когда строка создаСтся, Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ xmin устанавливаСтся Π² Π½ΠΎΠΌΠ΅Ρ€ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ, Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠ²ΡˆΠ΅ΠΉ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ INSERT, Π° xmax Π½Π΅ заполняСтся.

Когда строка удаляСтся, Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ xmax Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ вСрсии помСчаСтся Π½ΠΎΠΌΠ΅Ρ€ΠΎΠΌ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ, Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠ²ΡˆΠ΅ΠΉ DELETE.

Когда строка измСняСтся ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ UPDATE, фактичСски Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡŽΡ‚ΡΡ Π΄Π²Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ: DELETE ΠΈ INSERT. Π’ Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ вСрсии строки устанавливаСтся xmax, Ρ€Π°Π²Π½Ρ‹ΠΉ Π½ΠΎΠΌΠ΅Ρ€Ρƒ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ, Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠ²ΡˆΠ΅ΠΉ UPDATE. Π—Π°Ρ‚Π΅ΠΌ создаСтся новая вСрсия Ρ‚ΠΎΠΉ ΠΆΠ΅ строки; Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ 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)

ЗаглянСм Π² содСрТимоС страницы. Ѐункция heap_page_items Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ pageinspect позволяСт ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎΠ± указатСлях ΠΈ вСрсиях строк:

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

Π—Π°ΠΌΠ΅Ρ‚ΠΈΠΌ, Ρ‡Ρ‚ΠΎ словом heap (ΠΊΡƒΡ‡Π°) Π² 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. Π—Π΄Π΅ΡΡŒ ΠΎΠ½ Β«normalΒ» β€” это Π·Π½Π°Ρ‡ΠΈΡ‚, Ρ‡Ρ‚ΠΎ ΡƒΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ссылаСтся Π½Π° Π²Π΅Ρ€ΡΠΈΡŽ строки. Π”Ρ€ΡƒΠ³ΠΈΠ΅ значСния рассмотрим ΠΏΠΎΠ·ΠΆΠ΅.
  • Из всСх ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΎΠ½Π½Ρ‹Ρ… Π±ΠΈΡ‚ΠΎΠ² Π²Ρ‹Π΄Π΅Π»ΠΈΠ»ΠΈ ΠΏΠΎΠΊΠ° Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π΄Π²Π΅ ΠΏΠ°Ρ€Ρ‹. Π‘ΠΈΡ‚Ρ‹ 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 (commit log) ΠΈ это Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π΅Ρ‰Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π²ΡΡ‚Ρ€Π΅Ρ‡Π°Ρ‚ΡŒΡΡ Π² Ρ€Π°Π·Π½Ρ‹Ρ… мСстах).

XACT β€” Π½Π΅ Ρ‚Π°Π±Π»ΠΈΡ†Π° систСмного ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π°; это Ρ„Π°ΠΉΠ»Ρ‹ Π² ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π΅ PGDATA/pg_xact. Π’ Π½ΠΈΡ… для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ ΠΎΡ‚Π²Π΅Π΄Π΅Π½ΠΎ Π΄Π²Π° Π±ΠΈΡ‚Π°: committed ΠΈ aborted β€” Ρ‚ΠΎΡ‡Π½ΠΎ Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ Π² Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ΅ вСрсии строки. На нСсколько Ρ„Π°ΠΉΠ»ΠΎΠ² эта информация Ρ€Π°Π·Π±ΠΈΡ‚Π° ΠΈΡΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ для удобства, ΠΌΡ‹ Π΅Ρ‰Π΅ вСрнСмся ΠΊ этому вопросу, ΠΊΠΎΠ³Π΄Π° Π±ΡƒΠ΄Π΅ΠΌ Ρ€Π°ΡΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ Π·Π°ΠΌΠΎΡ€ΠΎΠ·ΠΊΡƒ. А Ρ€Π°Π±ΠΎΡ‚Π° с этими Ρ„Π°ΠΉΠ»Π°ΠΌΠΈ вСдСтся постранично, ΠΊΠ°ΠΊ ΠΈ со всСми Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ.

Π˜Ρ‚Π°ΠΊ, ΠΏΡ€ΠΈ фиксации Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ Π² XACT выставляСтся Π±ΠΈΡ‚ committed для Π΄Π°Π½Π½ΠΎΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ. И это всС, Ρ‡Ρ‚ΠΎ происходит ΠΏΡ€ΠΈ фиксации (ΠΏΡ€Π°Π²Π΄Π°, ΠΌΡ‹ ΠΏΠΎΠΊΠ° Π½Π΅ Π³ΠΎΠ²ΠΎΡ€ΠΈΠΌ ΠΏΡ€ΠΎ ΠΆΡƒΡ€Π½Π°Π» прСдзаписи).

Когда какая-Π»ΠΈΠ±ΠΎ другая транзакция обратится ΠΊ Ρ‚Π°Π±Π»ΠΈΡ‡Π½ΠΎΠΉ страницС, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‡Ρ‚ΠΎ смотрСли, Π΅ΠΉ придСтся ΠΎΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π½Π° нСсколько вопросов.

  1. Π—Π°Π²Π΅Ρ€ΡˆΠΈΠ»Π°ΡΡŒ Π»ΠΈ транзакция xmin? Если Π½Π΅Ρ‚, Ρ‚ΠΎ созданная вСрсия строки Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ Π²ΠΈΠ΄Π½Π°.
    Вакая ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° выполняСтся просмотром Π΅Ρ‰Π΅ ΠΎΠ΄Π½ΠΎΠΉ структуры, которая располагаСтся Π² ΠΎΠ±Ρ‰Π΅ΠΉ памяти экзСмпляра ΠΈ называСтся ProcArray. Π’ Π½Π΅ΠΉ находится список всСх Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… процСссов, ΠΈ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΡƒΠΊΠ°Π·Π°Π½ Π½ΠΎΠΌΠ΅Ρ€ Π΅Π³ΠΎ Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ (Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΠΉ) Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ.
  2. Если Π·Π°Π²Π΅Ρ€ΡˆΠΈΠ»Π°ΡΡŒ, Ρ‚ΠΎ ΠΊΠ°ΠΊ β€” фиксациСй ΠΈΠ»ΠΈ ΠΎΡ‚ΠΌΠ΅Π½ΠΎΠΉ? Если ΠΎΡ‚ΠΌΠ΅Π½ΠΎΠΉ, Ρ‚ΠΎ вСрсия строки Ρ‚ΠΎΠΆΠ΅ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Π²ΠΈΠ΄Π½Π°.
    Π’ΠΎΡ‚ для этого ΠΊΠ°ΠΊ Ρ€Π°Π· ΠΈ Π½ΡƒΠΆΠ΅Π½ XACT. Но, хотя послСдниС страницы XACT ΡΠΎΡ…Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π² Π±ΡƒΡ„Π΅Ρ€Π°Ρ… Π² ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΈΠ²Π½ΠΎΠΉ памяти, всС ΠΆΠ΅ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π· ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ 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. ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½Π΅Π΅ ΠΏΡ€ΠΎ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ΡŒ ΠΏΠΎΠ·ΠΆΠ΅. Пока ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΠΌ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ, Ρ‡Ρ‚ΠΎ число Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ строк Π½ΠΈΡ‡Π΅ΠΌ Π½Π΅ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΎ. Они Π½Π΅ Π·Π°Π½ΠΈΠΌΠ°ΡŽΡ‚ мСсто Π² ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΈΠ²Π½ΠΎΠΉ памяти ΠΈ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ систСмы Π½Π΅ страдаСт ΠΎΡ‚ ΠΈΡ… количСства. ΠŸΡ€Π°Π²Π΄Π°, Ρƒ β€œΠ΄Π»ΠΈΠ½Π½Ρ‹Ρ…β€ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΉ Π΅ΡΡ‚ΡŒ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ минусы, Π½ΠΎ ΠΎΠ± этом Ρ‚ΠΎΠΆΠ΅ ΠΏΠΎΠ·ΠΆΠ΅.

Π£Π΄Π°Π»ΠΈΠΌ строку.

=> 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 для Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ выставляСтся Π±ΠΈΡ‚ aborted. ΠžΡ‚ΠΌΠ΅Π½Π° выполняСтся Ρ‚Π°ΠΊ ΠΆΠ΅ быстро, ΠΊΠ°ΠΊ ΠΈ фиксация. Π₯ΠΎΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Π° ΠΈ называСтся 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 ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡŽ, ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰ΡƒΡŽ Β«ΡΠΊΠΎΠ½ΠΎΠΌΠΈΡ‚ΡŒΒ» Π½ΠΎΠΌΠ΅Ρ€Π° Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΉ.

Если транзакция Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‡ΠΈΡ‚Π°Π΅Ρ‚ Π΄Π°Π½Π½Ρ‹Π΅, Ρ‚ΠΎ ΠΎΠ½Π° Π½ΠΈΠΊΠ°ΠΊ Π½Π΅ влияСт Π½Π° Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ вСрсий строк. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Π²Π½Π°Ρ‡Π°Π»Π΅ ΠΎΠ±ΡΠ»ΡƒΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ процСсс Π²Ρ‹Π΄Π°Π΅Ρ‚ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΉ Π½ΠΎΠΌΠ΅Ρ€ (virtual 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), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ ΠΎΡ‚ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Ρ‡Π°ΡΡ‚ΡŒ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ, Π½Π΅ прСрывая Π΅Π΅ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ. Но это Π½Π΅ укладываСтся Π² ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½ΡƒΡŽ Π²Ρ‹ΡˆΠ΅ схСму, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ статус Ρƒ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ ΠΎΠ΄ΠΈΠ½ Π½Π° всС Π΅Π΅ измСнСния, Π° физичСски Π½ΠΈΠΊΠ°ΠΊΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Π΅ Π½Π΅ ΠΎΡ‚ΠΊΠ°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ΡΡ.

Π§Ρ‚ΠΎΠ±Ρ‹ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π», транзакция с Ρ‚ΠΎΡ‡ΠΊΠΎΠΉ сохранСния разбиваСтся Π½Π° нСсколько ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΉ (subtransaction), статусом ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎ.

Π’Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ ΠΈΠΌΠ΅ΡŽΡ‚ свой собствСнный Π½ΠΎΠΌΠ΅Ρ€ (бóльший, Ρ‡Π΅ΠΌ Π½ΠΎΠΌΠ΅Ρ€ основной Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ). Бтатус Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΉ записываСтся ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ Π² 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 фактичСски ставит ΠΏΠ΅Ρ€Π΅Π΄ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ Π½Π΅ΡΠ²Π½ΡƒΡŽ Ρ‚ΠΎΡ‡ΠΊΡƒ сохранСния, Π° Π² случаС сбоя ΠΈΠ½ΠΈΡ†ΠΈΠΈΡ€ΡƒΠ΅Ρ‚ ΠΎΡ‚ΠΊΠ°Ρ‚ ΠΊ Π½Π΅ΠΉ. Π’Π°ΠΊΠΎΠΉ Ρ€Π΅ΠΆΠΈΠΌ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ установка Ρ‚ΠΎΡ‡Π΅ΠΊ сохранСния (Π΄Π°ΠΆΠ΅ Π±Π΅Π· ΠΎΡ‚ΠΊΠ°Ρ‚Π° ΠΊ Π½ΠΈΠΌ) сопряТСна с сущСствСнными Π½Π°ΠΊΠ»Π°Π΄Π½Ρ‹ΠΌΠΈ расходами.

ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com