sql >> Database >  >> RDS >> PostgreSQL

Systeemkolommen in PostgreSQL begrijpen

Dus je zit met je handen over een toetsenbord en denkt "wat voor plezier kan ik hebben om mijn leven nog nieuwsgieriger te maken?..." Nou - maak een tafel natuurlijk!

vao=# create table nocol();
CREATE TABLE
vao=# select * from nocol;
--
(0 rows)

Wat is er leuk aan een tafel zonder gegevens?... Absoluut geen! Maar ik kan het gemakkelijk repareren:

vao=# insert into nocol default values;
INSERT 0 1

Het ziet er raar en nogal dom uit om een ​​tabel te hebben zonder kolommen en één rij. Om nog maar te zwijgen over het feit dat het niet duidelijk is welke "standaardwaarden" er zijn ingevoegd ... Nou - als je enkele regels uit documenten leest, blijkt dat "Alle kolommen zullen worden gevuld met hun standaardwaarden .” Toch heb ik geen kolommen! Nou - ik heb er zeker een paar:

vao=# select attname, attnum, atttypid::regtype, attisdropped::text from pg_attribute where attrelid = 'nocol'::regclass;
 attname  | attnum | atttypid | attisdropped 
----------+--------+----------+--------------
 tableoid |     -7 | oid      | false
 cmax     |     -6 | cid      | false
 xmax     |     -5 | xid      | false
 cmin     |     -4 | cid      | false
 xmin     |     -3 | xid      | false
 ctid     |     -1 | tid      | false
(6 rows)

Dus deze zes zijn zeker niet de ALTER TABLE DROP COLUMN-zombies omdat attisdropped onwaar is. Ook zie ik dat de typenaam van die kolommen eindigt met "id". Als u het onderste gedeelte van Object Identifier Types leest, krijgt u een idee. Een andere grappige observatie is - de -2 ontbreekt! Ik vraag me af waar ik het had kunnen verliezen - ik heb toch maar een tafel gemaakt! Hmm, welke object-ID ontbreekt in mijn tabel? Per definitie bedoel ik. Ik heb tuple-, commando- en xact-ID's. Nou, tenzij een "global over hele db identifier", zoals oid?.. Controleren is eenvoudig - ik zal een tabel maken met OIDS:

vao=# create table nocol_withoid() with oids;
CREATE TABLE
vao=# select attname, attnum, atttypid::regtype, attisdropped::text from pg_attribute where attrelid = 'nocol_withoid'::regclass;
 attname  | attnum | atttypid | attisdropped 
----------+--------+----------+--------------
 tableoid |     -7 | oid      | false
 cmax     |     -6 | cid      | false
 xmax     |     -5 | xid      | false
 cmin     |     -4 | cid      | false
 xmin     |     -3 | xid      | false
 oid      |     -2 | oid      | false
 ctid     |     -1 | tid      | false
(7 rows)

Voila! Dus de ontbrekende -2 ontbreekt inderdaad en daar houden we van. oids uitgeven voor gebruikte datarijen zou een slecht idee zijn, dus ik blijf spelen met een tafel zonder OIDS.

Wat ik heb? Ik heb 6 attributen na het maken van "geen kolomtabel" met (oids=false). Moet ik systeemkolommen gebruiken? Zo ja, waarom zijn ze dan een beetje verborgen? Nou - ik neem aan dat er niet zo breed geadverteerd wordt, omdat het gebruik niet intuïtief is en het gedrag in de toekomst kan veranderen. Bijvoorbeeld, na het zien van tuple id (ctid) denken sommigen misschien "ah - dit is een soort interne PK" (en dat is het ook):

vao=# select ctid from nocol;
 ctid  
-------
 (0,1)
(1 row)

De eerste cijfers (nul) staan ​​voor het paginanummer en de tweede (een) voor het tupelnummer. Ze zijn opeenvolgend:

vao=# insert into nocol default values;
INSERT 0 1
vao=# select ctid from nocol;
 ctid  
-------
 (0,1)
 (0,2)
(2 rows)

Maar deze reeks helpt je niet om te bepalen welke rij erna is aangekomen waarna:

vao=# alter table nocol add column i int;
ALTER TABLE
vao=# update nocol set i = substring(ctid::text from 4 for 1)::int;
UPDATE 2
vao=# select i, ctid from nocol;
 i | ctid  
---+-------
 1 | (0,3)
 2 | (0,4)
(2 rows)

Hier heb ik een kolom toegevoegd (om mijn rijen te identificeren) en deze gevuld met het eerste tuple-nummer (let op dat beide rijen fysiek zijn verplaatst)

vao=# delete from nocol where ctid = '(0,3)';
DELETE 1
vao=# vacuum nocol;
VACUUM
vao=# insert into nocol default values;
INSERT 0 1
vao=# select i, ctid from nocol;
 i | ctid  
---+-------
   | (0,1)
 2 | (0,4)
(2 rows)

Aha! (zei met stijgende intonatie) - hier heb ik een van mijn rijen verwijderd, het vacuüm op de arme tafel laten ontsnappen en een nieuwe rij ingevoegd. Het resultaat:de later toegevoegde rij staat in de eerste tupel op de eerste pagina, omdat Postgres wijselijk heeft besloten om de ruimte te besparen en de vrijgekomen ruimte opnieuw te gebruiken.

Dus het idee om ctid te gebruiken om de reeks ingevoerde rijen te krijgen, ziet er slecht uit. Tot op een bepaald niveau - als u in één transactie werkt, blijft de volgorde behouden - zullen nieuw getroffen rijen op dezelfde tafel een "grotere" ctid hebben. Natuurlijk worden na het vacuüm (autovacuüm) of als je het geluk hebt om HOT-updates eerder of net vrijgegeven hiaten opnieuw te hebben gebruikt - de volgorde doorbrekend. Maar vrees niet - er waren zes verborgen attributen, niet één!

vao=# select i, ctid, xmin from nocol;
 i | ctid  | xmin  
---+-------+-------
   | (0,1) | 26211
 2 | (0,4) | 26209
(2 rows)

Als ik de xmin controleer, zal ik zien dat de transactie-ID die de laatst ingevoegde rij heeft geïntroduceerd (+2) hoger is (+1 was de verwijderde rij). Dus voor de sequentiële rij-ID zou ik een totaal ander attribuut kunnen gebruiken! Natuurlijk is het niet zo eenvoudig, anders zou dergelijk gebruik worden aangemoedigd. De xmin-kolom vóór 9.4 is eigenlijk overschreven om te beschermen tegen xid-omhulling. Waarom zo ingewikkeld? De MVCC in Postgres is erg slim en de methoden eromheen worden in de loop van de tijd beter. Natuurlijk brengt het complexiteit met zich mee. Helaas. Sommige mensen willen zelfs systeemkolommen vermijden. Dubbel helaas. Omdat systeemkolommen cool en goed gedocumenteerd zijn. Het allerbeste kenmerk (onthoud dat ik oids oversla) is tableoid:

vao=# select i, tableoid from nocol;
 i | tableoid 
---+----------
   |   253952
 2 |   253952
(2 rows)
Download de whitepaper vandaag PostgreSQL-beheer en -automatisering met ClusterControlLees wat u moet weten om PostgreSQL te implementeren, bewaken, beheren en schalenDownload de whitepaper

Het ziet er nutteloos uit met DEZELFDE waarde in elke rij - nietwaar? En toch was het een tijdje geleden een erg populair attribuut - toen we allemaal partitionering aan het bouwen waren met behulp van regels en overgenomen tabellen. Hoe zou je debuggen uit welke tabel de rij komt, zo niet met tableoid? Dus wanneer u regels, weergaven (dezelfde regels) of UNION gebruikt, helpt het tableoid-attribuut u de bron te identificeren:

vao=# insert into nocol_withoid default values;
INSERT 253967 1
vao=# select ctid, tableoid from nocol union select ctid, tableoid from nocol_withoid ;
 ctid  | tableoid 
-------+----------
 (0,1) |   253952
 (0,1) |   253961
 (0,4) |   253952
(3 rows)

Wauw wat was dat? Ik ben er zo aan gewend geraakt om INSERT 0 1 te zien dat mijn psql-uitvoer er raar uitzag! Ah - waar - ik heb een tabel gemaakt met oids en heb gewoon wanhopig zinloos één (253967) identifier gebruikt! Nou - niet helemaal zinloos (hoewel wanhopig) - de select retourneert twee rijen met dezelfde ctid (0,1) - niet verrassend - ik selecteer uit twee tabellen en voeg vervolgens resultaten toe aan elkaar, dus de kans om dezelfde ctid te hebben is niet zo laag. Het laatste om te vermelden is dat ik weer typen object-ID's kan gebruiken om het mooi weer te geven:

vao=# select ctid, tableoid::regclass from nocol union select ctid, tableoid from nocol_withoid ;
 ctid  |   tableoid    
-------+---------------
 (0,1) | nocol
 (0,1) | nocol_withoid
 (0,4) | nocol
(3 rows)

Aha! (zei met stijgende intonatie) - Dus dat is de manier om de gegevensbron hier duidelijk vast te pinnen!

Eindelijk nog een zeer populair en interessant gebruik - definiëren welke rij is ingevoegd en welke omhoog is geschoven:

vao=# update nocol set i = 0 where i is null;
UPDATE 1
vao=# alter table nocol alter COLUMN i set not null;
ALTER TABLE
vao=# alter table nocol add constraint pk primary key (i);
ALTER TABLE

Nu we een PK hebben, kan ik de ON CONFLICT-richtlijn gebruiken:

vao=# insert into nocol values(0),(-1) on conflict(i) do update set i = extract(epoch from now()) returning i, xmax;
     i      |   xmax    
------------+-----------
 1534433974 |     26281
         -1 |         0
(2 rows)
Gerelateerde bronnen ClusterControl voor PostgreSQL De PostgreSQL-systeemcatalogus begrijpen en lezen Een overzicht van database-indexering in PostgreSQL

Waarom zo blij? Omdat ik (met enige vertrouwelijkheid) kan vertellen dat die rij met xmax niet gelijk aan nul is dat deze is bijgewerkt. En denk niet dat het voor de hand ligt - het ziet er zo uit, alleen omdat ik Unixtime voor PK heb gebruikt, dus het ziet er echt anders uit dan waarden van één cijfer. Stel je voor dat je zo'n ON CONFLICT-draai op een grote set doet en er is geen logische manier om te identificeren welke waarde conflict had en welke - niet. xmax heeft tonnen DBA's geholpen in moeilijke tijden. En de beste beschrijving van hoe het werkt, zou ik hier aanbevelen - net zoals ik alle drie de deelnemers aan de discussie (Abelisto, Erwin en Laurenz) zou aanraden om te lezen over andere postgres-tagvragen en -antwoorden op SO.

Dat is het.

tableoid, xmax, xmin en ctid zijn goede vrienden van elke DBA. Om cmax, cmin en oid niet te beledigen - ze zijn ook net zo goede vrienden! Maar dit is genoeg voor een kleine recensie en ik wil nu mijn handen van het toetsenbord halen.


  1. Waarom klaagt Oracle 10g niet over dubbelzinnigheid van kolommen?

  2. PostgreSQL 9.6:parallelle sequentiële scan

  3. PostgreSQL-fout bij het maken van een extensie

  4. SIN() Functie in Oracle