sql >> Databáze >  >> RDS >> PostgreSQL

PostgreSQL Upsert rozlišuje vložené a aktualizované řádky pomocí systémových sloupců XMIN, XMAX a dalších

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:

  1. 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 a xmax může vidět tuple. Starou n-tici lze bezpečně smazat, pokud neexistuje žádná transakce s ID transakce menším než xmax .

  2. 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 .

  1. Na ukazatel čáry (lp ) 1 najdeme starou, aktualizovanou n-tici. Původně měl ctid = (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á vydala INSERT ... ON CONFLICT ). Tato n-tice již není viditelná a bude odstraněna během VACUUM .

    t_infomask ukazuje, že oba xmin a xmax 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 (viz src/backend/access/heap/README.HOT ).

  2. 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 aktualizace xmin je platný a xmax obsahuje KEY SHARE zámek řádku (který již není relevantní, protože transakce byla dokončena). Tento zámek řádku byl použit během INSERT ... ON CONFLICT zpracovává se. t_infomask2 ukazuje, že se jedná o HOT n-tice.

  3. U ukazatele řádku 3 vidíme nově vložený řádek.

    t_infomask ukazuje, že xmin je platný a xmax 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é.




  1. Jak mohu efektivně vybrat předchozí nenulovou hodnotu?

  2. Primární klíče s Apache Spark

  3. Jak funguje SQRT() v MariaDB

  4. Jak zabít všechny aktivní a neaktivní relace oracle pro uživatele