sql >> Database teknologi >  >> RDS >> PostgreSQL

PostgreSQL Upsert differentiere indsatte og opdaterede rækker ved hjælp af systemkolonner XMIN, XMAX og andre

Jeg synes, det er et interessant spørgsmål, som fortjener et uddybende svar; tak over med mig, hvis det er lidt langt.

Kort sagt:Dit gæt er rigtigt, og du kan bruge følgende RETURNING klausul for at bestemme, om rækken blev indsat og ikke opdateret:

RETURNERING (xmax =0) SOM indsat 

Nu den detaljerede forklaring:

Når en række opdateres, ændrer PostgreSQL ikke dataene, men opretter en ny version af rækken; den gamle version vil blive slettet af autovacuum når det ikke længere er nødvendigt. En version af en række kaldes en tuple , så i PostgreSQL kan der være mere end én tupler pr. række.

xmax tjener to forskellige formål:

  1. Som det fremgår af dokumentationen, kan det være transaktions-id'et for transaktionen, der har slettet (eller opdateret) tuplen ("tuple" er et andet ord for "række"). Kun transaktioner med et transaktions-id mellem xmin og xmax kan se tupelen. En gammel tuple kan slettes sikkert, hvis der ikke er nogen transaktion med et transaktions-id mindre end xmax .

  2. xmax bruges også til at gemme rækkelåse . I PostgreSQL gemmes rækkelåse ikke i låsetabellen, men i tuple for at undgå overløb af låsetabellen.
    Hvis kun én transaktion har en lås på rækken, xmax vil indeholde transaktions-id'et for låsetransaktionen. Hvis mere end én transaktion har en lås på rækken, xmax indeholder nummeret på en såkaldt multixact , som er en datastruktur, der igen indeholder transaktions-id'erne for låsetransaktionerne.

Dokumentationen for xmax er ikke fuldstændig, fordi den nøjagtige betydning af dette felt betragtes som en implementeringsdetalje og kan ikke forstås uden at kende t_infomask af tuplen, som ikke umiddelbart er synlig via SQL.

Du kan installere bidragsmodulet pageinspect for at se dette og andre felter i en tupel.

Jeg kørte dit eksempel, og det er det, jeg ser, når jeg bruger heap_page_items funktion til at undersøge detaljer (transaktions-id-numrene er selvfølgelig forskellige i mit tilfælde):

SELECT *, ctid, xmin, xmax FROM t;┌───┬────┬───────┬─────────────── ─ Hver │ 1 │ 11 │ (0,2) │ 102508 │ 102508 ││ 2 │ 22 │ (0,3) │ 102508 │ 0 │└ Hver ──────┴────────┘(2 rækker)VÆLG lp, lp_off, t_xmin, t_xmax, t_ctid, to_hex(t_infomask) AS t_2_page(as_t_infomaske) AS t_2_page(AS_2_side) .t', 0));┌────┬────────┬────────┬──└ ─ ovearat ─ Hver ──øre ommearats───┤│ 1 │ 8160 │ 102507 │ 102508 │ (0,2) │ 500 │ 4002 ││ 2 │ 8128 │ 102508 │ 102508 │ (0,2) │ 2190 │ 8002 ││ 3 │ 8096 │ 102508 │ 0 │ (0,3) │ 900 │ 2 │└ Hver ┴────────┴────────────┴─────────────)s kode 3

Betydningen af ​​t_infomask og t_infomask2 kan findes i src/include/access/htup_details.h . lp_off er forskydningen af ​​tuple-dataene på siden, og t_ctid er det nuværende tuple-id som består af sidetallet og et tupelnummer på siden. Da tabellen er nyoprettet, er alle data på side 0.

Lad mig diskutere de tre rækker, der returneres af heap_page_items .

  1. Ved linjemarkør (lp ) 1 finder vi den gamle, opdaterede tupel. Den havde oprindeligt ctid =(0,1) , men det blev ændret til at indeholde tuple-id'et for den aktuelle version under opdateringen. Tuple blev oprettet af transaktion 102507 og ugyldiggjort af transaktion 102508 (transaktionen, der udstedte INSERT ... ON CONFLICT ). Denne tuple er ikke længere synlig og vil blive fjernet under VACUUM .

    t_infomask viser, at både xmin og xmax hører til forpligtede transaktioner og viser følgelig, hvornår tuplerne blev oprettet og slettet. t_infomask2 viser, at tuppelen blev opdateret med en HOT (kun heap-tuple ) update, hvilket betyder, at den opdaterede tuple er på samme side som den originale tuple, og ingen indekseret kolonne blev ændret (se src/backend/access/heap/README.HOT ).

  2. Ved linjemarkør 2 ser vi den nye, opdaterede tuple, der blev oprettet ved transaktionen INSERT ... ON CONFLICT (transaktion 102508).

    t_infomask viser, at denne tuple er resultatet af en opdatering, xmin er gyldig, og xmax indeholder en KEY SHARE rækkelås (som ikke længere er relevant, da transaktionen er gennemført). Denne rækkelås blev taget under INSERT ... ON CONFLICT forarbejdning. t_infomask2 viser, at dette er en HOT tuple.

  3. Ved linjemarkør 3 ser vi den nyindsatte række.

    t_infomask viser, at xmin er gyldig og xmax er ugyldig. xmax er sat til 0, fordi denne værdi altid bruges til nyligt indsatte tupler.

xmax er ikke nul af den opdaterede række er en implementeringsartefakt forårsaget af en rækkelås. Det er tænkeligt, at INSERT ... ON CONFLICT bliver genimplementeret en dag, så denne adfærd ændrer sig, men det tror jeg er usandsynligt.




  1. Sammenlign to MySQL-databaser

  2. Sådan omdøbes en kolonne i SQL

  3. Tips og tricks ved hjælp af revisionslogning til MariaDB

  4. Android Room Library kan ikke kopiere databasen fra aktiv