Myslím, že je to zajímavá otázka, která si zaslouží hloubkovou odpověď; prosím mějte se mnou trpělivost, pokud je to trochu zdlouhavé.
Stručně řečeno:Váš odhad je správný a můžete použít následující RETURNING
klauzule k určení, zda byl řádek vložen a nebyl aktualizován:
RETURNING (xmax = 0) AS inserted
Nyní podrobné vysvětlení:
Když je řádek aktualizován, PostgreSQL neupraví data, ale vytvoří novou verzi z řady; stará verze bude smazána pomocí autovakuování když už to není potřeba. Verze řádku se nazývá n-tice , takže v PostgreSQL může být více než jedna n-tice na řádek.
xmax
slouží dvěma různým účelům:
-
Jak je uvedeno v dokumentaci, může to být ID transakce transakce, která odstranila (nebo aktualizovala) n-tici („n-tice“ je jiné slovo pro „řádek“). Pouze transakce s ID transakce mezi
xmin
axmax
může vidět tuple. Starou n-tici lze bezpečně smazat, pokud neexistuje žádná transakce s ID transakce menším nežxmax
. -
xmax
se také používá k uložení zámků řádků . V PostgreSQL se zámky řádků neukládají do tabulky zámků, ale do n-tice, aby se zabránilo přetečení tabulky zámků.
Pokud má na řádku zámek pouze jedna transakce,xmax
bude obsahovat ID transakce uzamčení transakce. Pokud má více než jedna transakce zámek na řádku,xmax
obsahuje číslo tzv. multixaktu , což je datová struktura, která zase obsahuje ID transakcí zamykacích transakcí.
Dokumentace xmax
není kompletní, protože přesný význam tohoto pole je považován za detail implementace a nelze mu porozumět bez znalosti t_infomask
n-tice, která není okamžitě viditelná přes SQL.
Můžete nainstalovat modul contrib pageinspect
pro zobrazení tohoto a dalších polí n-tice.
Spustil jsem váš příklad a toto vidím, když použiji heap_page_items
funkce pro prozkoumání podrobností (identifikační čísla transakcí se v mém případě samozřejmě liší):
SELECT *, ctid, xmin, xmax FROM t;
┌───┬────┬───────┬────────┬────────┐
│ i │ x │ ctid │ xmin │ xmax │
├───┼────┼───────┼────────┼────────┤
│ 1 │ 11 │ (0,2) │ 102508 │ 102508 │
│ 2 │ 22 │ (0,3) │ 102508 │ 0 │
└───┴────┴───────┴────────┴────────┘
(2 rows)
SELECT lp, lp_off, t_xmin, t_xmax, t_ctid,
to_hex(t_infomask) AS t_infomask, to_hex(t_infomask2) AS t_infomask2
FROM heap_page_items(get_raw_page('laurenz.t', 0));
┌────┬────────┬────────┬────────┬────────┬────────────┬─────────────┐
│ lp │ lp_off │ t_xmin │ t_xmax │ t_ctid │ t_infomask │ t_infomask2 │
├────┼────────┼────────┼────────┼────────┼────────────┼─────────────┤
│ 1 │ 8160 │ 102507 │ 102508 │ (0,2) │ 500 │ 4002 │
│ 2 │ 8128 │ 102508 │ 102508 │ (0,2) │ 2190 │ 8002 │
│ 3 │ 8096 │ 102508 │ 0 │ (0,3) │ 900 │ 2 │
└────┴────────┴────────┴────────┴────────┴────────────┴─────────────┘
(3 rows)
Význam t_infomask
a t_infomask2
lze nalézt v src/include/access/htup_details.h
. lp_off
je offset dat n-tice na stránce a t_ctid
je aktuální ID n-tice který se skládá z čísla stránky a čísla n-tice v rámci stránky. Protože byla tabulka nově vytvořena, všechna data jsou na stránce 0.
Dovolte mi probrat tři řádky vrácené heap_page_items
.
-
Na ukazatel čáry (
lp
) 1 najdeme starou, aktualizovanou n-tici. Původně mělctid = (0,1)
, ale to bylo během aktualizace upraveno tak, aby obsahovalo ID n-tice aktuální verze. The Tuple byl vytvořen transakcí 102507 a zrušen transakcí 102508 (transakce, která vydalaINSERT ... ON CONFLICT
). Tato n-tice již není viditelná a bude odstraněna běhemVACUUM
.t_infomask
ukazuje, že obaxmin
axmax
patří k potvrzeným transakcím a následně zobrazí, kdy byly n-tice vytvořeny a odstraněny.t_infomask2
ukazuje, že n-tice byla aktualizována pomocí HOT (n-tice pouze pro haldu ) update, což znamená, že aktualizovaná n-tice je na stejné stránce jako původní n-tice a nebyl změněn žádný indexovaný sloupec (vizsrc/backend/access/heap/README.HOT
). -
Na řádku ukazatel 2 vidíme novou, aktualizovanou n-tici, která byla vytvořena transakcí
INSERT ... ON CONFLICT
(transakce 102508).t_infomask
ukazuje, že tato n-tice je výsledkem aktualizacexmin
je platný axmax
obsahujeKEY SHARE
zámek řádku (který již není relevantní, protože transakce byla dokončena). Tento zámek řádku byl použit běhemINSERT ... ON CONFLICT
zpracovává se.t_infomask2
ukazuje, že se jedná o HOT n-tice. -
U ukazatele řádku 3 vidíme nově vložený řádek.
t_infomask
ukazuje, žexmin
je platný axmax
je neplatný.xmax
je nastavena na 0, protože tato hodnota se vždy používá pro nově vložené n-tice.
Tedy nenulové xmax
aktualizovaného řádku je implementační artefakt způsobený zámkem řádku. Je možné, že INSERT ... ON CONFLICT
je jednoho dne znovu implementován, aby se toto chování změnilo, ale myslím, že to je nepravděpodobné.