sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL Upsert maakt onderscheid tussen ingevoegde en bijgewerkte rijen met behulp van systeemkolommen XMIN, XMAX en andere

Ik denk dat dit een interessante vraag is die een diepgaand antwoord verdient; heb alsjeblieft geduld met me als het een beetje lang is.

In het kort:uw gok is juist, en u kunt de volgende RETURNING . gebruiken clausule om te bepalen of de rij is ingevoegd en niet is bijgewerkt:

RETURNING (xmax = 0) AS inserted

Nu de gedetailleerde uitleg:

Wanneer een rij wordt bijgewerkt, wijzigt PostgreSQL de gegevens niet, maar maakt het een nieuwe versie van de rij; de oude versie wordt verwijderd door autovacuum wanneer het niet meer nodig is. Een versie van een rij heet een tupel , dus in PostgreSQL kunnen er meer dan één tuples per rij zijn.

xmax heeft twee verschillende doelen:

  1. Zoals vermeld in de documentatie, kan het de transactie-ID van de transactie zijn die de tuple heeft verwijderd (of bijgewerkt) (“tuple” is een ander woord voor “rij”). Alleen transacties met een transactie-ID tussen xmin en xmax kan de tupel zien. Een oude tuple kan veilig worden verwijderd als er geen transactie is met een transactie-ID kleiner dan xmax .

  2. xmax wordt ook gebruikt om rijvergrendelingen op te slaan . In PostgreSQL worden rijvergrendelingen niet opgeslagen in de vergrendelingstabel, maar in de tupel om overloop van de vergrendelingstabel te voorkomen.
    Als slechts één transactie een vergrendeling op de rij heeft, xmax bevat de transactie-ID van de vergrendelingstransactie. Als meer dan één transactie een slot op de rij heeft, xmax bevat het nummer van een zogenaamde multixact , wat een gegevensstructuur is die op zijn beurt de transactie-ID's van de sluittransacties bevat.

De documentatie van xmax is niet volledig, omdat de exacte betekenis van dit veld wordt beschouwd als een implementatiedetail en niet kan worden begrepen zonder t_infomask te kennen van de tuple, die niet direct zichtbaar is via SQL.

U kunt de bijdragemodule pageinspect . installeren om deze en andere velden van een tuple te bekijken.

Ik heb je voorbeeld uitgevoerd en dit is wat ik zie als ik de heap_page_items gebruik functie om details te onderzoeken (de transactie-ID-nummers zijn natuurlijk anders in mijn geval):

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)

De betekenis van t_infomask en t_infomask2 is te vinden in src/include/access/htup_details.h . lp_off is de offset van de tuple-gegevens in de pagina, en t_ctid is de huidige tuple-ID die bestaat uit het paginanummer en een tupelnummer binnen de pagina. Omdat de tabel nieuw is gemaakt, staan ​​alle gegevens op pagina 0.

Laat me de drie rijen bespreken die worden geretourneerd door heap_page_items .

  1. Bij regelaanwijzer (lp ) 1 vinden we de oude, bijgewerkte tuple. Het had oorspronkelijk ctid = (0,1) , maar dat werd tijdens de update gewijzigd om de tuple-ID van de huidige versie te bevatten. De Tuple is gemaakt door transactie 102507 en ongeldig gemaakt door transactie 102508 (de transactie die de INSERT ... ON CONFLICT heeft uitgegeven ). Deze tuple is niet meer zichtbaar en wordt verwijderd tijdens VACUUM .

    t_infomask laat zien dat zowel xmin en xmax behoren tot vastgelegde transacties en laten dus zien wanneer de tuples zijn gemaakt en verwijderd. t_infomask2 laat zien dat de tuple is bijgewerkt met een HOT (alleen heap tuple .) ) update, wat betekent dat de bijgewerkte tuple op dezelfde pagina staat als de originele tuple en dat er geen geïndexeerde kolom is gewijzigd (zie src/backend/access/heap/README.HOT ).

  2. Bij regelaanwijzer 2 zien we de nieuwe, bijgewerkte tuple die is gemaakt door transactie de INSERT ... ON CONFLICT (transactie 102508).

    t_infomask laat zien dat deze tuple het resultaat is van een update, xmin geldig is, en xmax bevat een KEY SHARE rijslot (wat niet langer relevant is sinds de transactie is voltooid). Dit rijslot is genomen tijdens INSERT ... ON CONFLICT verwerken. t_infomask2 laat zien dat dit een HEET tuple is.

  3. Bij lijnaanwijzer 3 zien we de nieuw ingevoegde rij.

    t_infomask laat zien dat xmin is geldig en xmax is ongeldig. xmax is ingesteld op 0 omdat deze waarde altijd wordt gebruikt voor nieuw ingevoegde tuples.

Dus de niet-nul xmax van de bijgewerkte rij is een implementatieartefact veroorzaakt door een rijvergrendeling. Het is denkbaar dat INSERT ... ON CONFLICT wordt op een dag opnieuw geïmplementeerd zodat dit gedrag verandert, maar ik denk dat dat onwaarschijnlijk is.




  1. Hoe databasenaam opvragen in Oracle SQL Developer?

  2. Opgeslagen procedure verwijderen in MySQL

  3. SQLite verwijderen

  4. PostgreSQL-afstemming:belangrijke dingen om de prestaties te verbeteren