Takže sedíte s rukama nad klávesnicí a přemýšlíte:„Jakou zábavu si můžu užít, aby byl můj život ještě zvědavější?...“ No – samozřejmě vytvořte stůl!
vao=# create table nocol();
CREATE TABLE
vao=# select * from nocol;
--
(0 rows)
Jaká je legrace na tabulce bez dat?... Absolutně žádná! Ale mohu to snadno opravit:
vao=# insert into nocol default values;
INSERT 0 1
Vypadá to divně a docela hloupě mít tabulku bez sloupců a jeden řádek. Nemluvě o tom, že není jasné, jaké „výchozí hodnoty“ tam byly vloženy… No – přečtením několika řádků z dokumentů zjistíte, že „Všechny sloupce budou vyplněny svými výchozími hodnotami .“ Přesto nemám žádné sloupce! No - určitě nějaké mám:
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)
Těchto šest tedy rozhodně nejsou zombie ALTER TABLE DROP COLUMN, protože attisdropped je falešné. Také vidím, že název typu těchto sloupců končí „id“. Nápad získáte při čtení spodní části Typy identifikátorů objektu. Další vtipný postřeh je - chybí -2! Zajímalo by mě, kde jsem to mohl ztratit - přeci jen jsem vytvořil stůl! Hm, jaký identifikátor objektu chybí v mé tabulce? Definicí myslím. Mám id tuple, command a xact. Tedy pokud nějaký „globální přes celý db identifikátor“, jako oid?.. Kontrola je snadná – vytvořím tabulku s 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! Takže chybějící -2 opravdu chybí a to se nám líbí. Utrácet oidy za použité datové řádky by byl špatný nápad, takže budu dál hrát se stolem bez OIDS.
Co mám? Po vytvoření „žádné tabulky sloupců“ s (oids=false) mám 6 atributů. Mám používat systémové sloupce? Pokud ano, proč jsou tak trochu skryté? No - předpokládám, že nejsou tak široce inzerované, protože použití není intuitivní a chování se může v budoucnu změnit. Například poté, co viděli n-tici id (ctid), by si někteří mohli myslet „ah – toto je druh interního PK“ (a tak trochu je):
vao=# select ctid from nocol;
ctid
-------
(0,1)
(1 row)
První číslice (nula) znamenají číslo stránky a druhá (jedna) n-tici. Jsou sekvenční:
vao=# insert into nocol default values;
INSERT 0 1
vao=# select ctid from nocol;
ctid
-------
(0,1)
(0,2)
(2 rows)
Ale tato sekvence vám nepomůže ani určit, který řádek přišel po kterém:
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)
Zde jsem přidal sloupec (pro identifikaci mých řádků) a vyplnil jej počátečním číslem n-tice (nezapomeňte, že oba řádky byly fyzicky přesunuty)
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! (řeklo se stoupající intonací) - zde jsem smazal jeden ze svých řádků, vypustil vakuum na ubohém stole a vložil nový řádek. Výsledek – později přidaný řádek je na první stránce první n-tice, protože Postgres se moudře rozhodl ušetřit místo a znovu využít uvolněné místo.
Takže nápad použít ctid pro zavedení sekvence řádků vypadá špatně. Do určité úrovně – pokud pracujete v jedné transakci, sekvence zůstává – nově ovlivněné řádky ve stejné tabulce budou mít „větší“ ctid. Samozřejmě po vakuu (autovakuu) nebo pokud budete mít to štěstí, že máte HOT aktualizace dříve nebo právě uvolněné mezery, budou znovu použity - porušení sekvenčního pořadí. Ale nebojte se – bylo tam šest skrytých atributů, ne jeden!
vao=# select i, ctid, xmin from nocol;
i | ctid | xmin
---+-------+-------
| (0,1) | 26211
2 | (0,4) | 26209
(2 rows)
Pokud zkontroluji xmin, uvidím, že ID transakce, které uvedlo poslední vložený řádek, je (+2) vyšší (+1 byl odstraněný řádek). Takže pro sekvenční identifikátor řádku bych mohl použít úplně jiný atribut! Samozřejmě to není tak jednoduché, jinak by takové použití bylo podporováno. Sloupec xmin před 9.4 byl ve skutečnosti přepsán, aby byl chráněn před xid wraparound. Proč tak složitě? MVCC v Postgresu je velmi chytré a metody kolem něj se postupem času zlepšují. Samozřejmě to přináší složitost. Běda. Někteří lidé se dokonce chtějí vyhnout systémovým kolonám. Dvojí běda. Protože systémové sloupce jsou cool a dobře zdokumentované. Úplně nejvyšší atribut (pamatujte, že vynechávám oids) je tableoid:
vao=# select i, tableoid from nocol;
i | tableoid
---+----------
| 253952
2 | 253952
(2 rows)
Stáhněte si Whitepaper Today Správa a automatizace PostgreSQL s ClusterControlZjistěte, co potřebujete vědět k nasazení, monitorování, správě a škálování PostgreSQLStáhněte si Whitepaper Zdá se zbytečné mít STEJNOU hodnotu v každém řádku – že? A ještě před chvílí to byl velmi oblíbený atribut - když jsme všichni budovali rozdělení pomocí pravidel a zděděných tabulek. Jak byste ladili, ze které tabulky řádek pochází, pokud ne pomocí tabulky? Takže když používáte pravidla, pohledy (stejná pravidla) nebo UNION, atribut tableoid vám pomůže identifikovat zdroj:
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)
Páni, co to bylo? Už jsem si tolik zvykl na INSERT 0 1, že můj výstup psql vypadal divně! Aha - pravda - vytvořil jsem tabulku s oidy a jen jsem zoufale nesmyslně použil jeden (253967) identifikátor! No - ne úplně nesmyslně (i když zoufale) - select vrací dva řádky se stejným ctid (0,1) - není divu - vybírám ze dvou tabulek a pak přidávám výsledky jednu do druhé, takže mám šanci mít stejný ctid není tak nízká. Poslední věc, kterou je třeba zmínit, je, že mohu znovu použít typy identifikátorů objektů, abych to pěkně ukázal:
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! (řeklo se stoupající intonací) – Takže to je způsob, jak sem jasně připnout zdroj dat!
Konečně další velmi oblíbené a zajímavé použití - definování, který řádek byl vložen a který upserted:
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
Nyní, když máme PK, mohu použít direktivu ON CONFLICT:
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)
Související zdroje ClusterControl for PostgreSQL Porozumění a čtení katalogu systému PostgreSQL Přehled indexování databáze v PostgreSQL Proč tak šťastný? Protože mohu říct (s určitou důvěrností), že řádek s xmax se nerovná nule, že byl aktualizován. A nemyslete si, že je to samozřejmé – vypadá to tak jen proto, že jsem pro PK použil unixtime, takže to vypadá opravdu jinak než jednociferné hodnoty. Představte si, že uděláte takový obrat ON CONFLICT na velké sadě a neexistuje žádný logický způsob, jak určit, která hodnota byla v konfliktu a která ne. xmax pomohl tunám DBA v těžkých časech. A nejlepší popis toho, jak to funguje, bych doporučil zde – stejně jako bych všem třem účastníkům diskuse (Abelisto, Erwin a Laurenz) doporučil, aby si je přečetli v dalších otázkách a odpovědích na tagy postgres na SO.
To je ono.
tableoid, xmax, xmin a ctid jsou dobrými přáteli jakéhokoli DBA. Abych neurážel cmax, cmin a oid – jsou stejně dobří přátelé! Ale to stačí na malou recenzi a chci hned dát ruce pryč od klávesnice.