PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Разбиране на системните колони в PostgreSQL

Така че седите с ръце над клавиатура и си мислите „какво забавление мога да се забавлявам, за да направя живота си още по-любопитен?..” Е – създайте маса, разбира се!

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

Какво забавно е с таблица без данни?.. Абсолютно никакви! Но мога лесно да го поправя:

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

Изглежда странно и доста глупаво да имаш таблица без колони и един ред. Да не говорим, че не е ясно какви „стойности по подразбиране“ са били вмъкнати… Е – четенето на няколко реда от документи разкрива, че „Всички колони ще бъдат запълнени със стойностите им по подразбиране ” И все пак нямам колони! Е - със сигурност имам някои:

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)

Така че тези шест определено не са зомбита от ALTER TABLE DROP COLUMN, защото attisdropped е фалшиво. Също така виждам, че името на типа на тези колони завършва с „id“. Четенето на долния раздел на Типовете идентификатори на обекта ще даде идеята. Друго смешно наблюдение е - липсва -2! Чудя се къде бих могъл да го загубя - все пак току-що създадох таблица! Хм, какъв идентификатор на обект липсва в моята таблица? По дефиниция имам предвид. Имам tuple, command и xact идентификатори. Е, освен ако някакъв "глобален идентификатор за цял db", като oid?.. Проверката е лесна - ще създам таблица с 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)

Вуаля! Така че липсващото -2 наистина липсва и ни харесва. Разходването на oids за използвани редове с данни би било лоша идея, така че ще продължа да играя с таблица без OIDS.

какво имам? Имам 6 атрибута, след като създадох „таблица без колони“ с (oids=false). Трябва ли да използвам системни колони? Ако е така, защо са някак скрити? Е - предполагам, че не са толкова широко рекламирани, защото употребата не е интуитивна и поведението може да се промени в бъдеще. Например, след като видят идентификатор на кортежа (ctid), някои може да си помислят „ах – това е нещо като вътрешен PK“ (и някак е):

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

Първите цифри (нула) означават номера на страницата, а втората (единицата) означават номера на кортежа. Те са последователни:

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

Но тази последователност няма да ви помогне да определите дори кой ред е пристигнал след кой:

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)

Тук добавих колона (за да идентифицирам моите редове) и я попълних с първоначален номер на кортежа (имайте предвид, че и двата реда бяха физически преместени)

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)

Аха! (казано с нарастваща интонация) - тук изтрих един от редовете си, пуснах вакуума на бедната маса и вмъкнах нов ред. Резултатът - добавеният по-късно ред е в първата страница на кортежа, тъй като Postgres разумно реши да спести място и да използва повторно освободеното място.

Така че идеята да се използва ctid за получаване на въведената последователност от редове изглежда лоша. До някакво ниво - ако работите в една транзакция, последователността остава - новозасегнатите редове в същата таблица ще имат "по-голям" ctid. Разбира се, след вакуумиране (автовакуум) или ако имате достатъчно късмет да имате ГОРЕЩИ актуализации по-рано или току-що освободените пропуски ще бъдат използвани повторно - нарушаване на последователния ред. Но не се страхувайте – имаше шест скрити атрибута, а не един!

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

Ако проверя xmin, ще видя, че идентификаторът на транзакцията, който въведе последния вмъкнат ред, е (+2) по-висок (+1 беше изтритият ред). Така че за последователен идентификатор на ред може да използвам напълно различен атрибут! Разбира се, не е толкова просто, в противен случай подобна употреба би била насърчена. Колоната xmin преди 9.4 всъщност беше презаписана, за да се предпази от обвиване на xid. Защо толкова сложно? MVCC в Postgres е много интелигентен и методите около него се подобряват с времето. Разбира се, това носи сложност. уви. Някои хора дори искат да избегнат системните колони. Двойно уви. Тъй като системните колони са готини и добре документирани. Най-горният атрибут (не забравяйте, че пропускам oids) е tableoid:

vao=# select i, tableoid from nocol;
 i | tableoid 
---+----------
   |   253952
 2 |   253952
(2 rows)
Изтеглете Бялата книга днес Управление и автоматизация на PostgreSQL с ClusterControl Научете какво трябва да знаете, за да внедрите, наблюдавате, управлявате и мащабирате PostgreSQLD Изтеглете Бялата книга

Изглежда безполезно да има ЕДНАВА стойност във всеки ред - нали? И все пак преди малко това беше много популярен атрибут - когато всички изграждахме разделяне, използвайки правила и наследени таблици. Как бихте отстранили грешките от коя таблица идва редът, ако не с tableoid? Така че, когато използвате правила, изгледи (същите правила) или UNION, атрибутът tableoid ви помага да идентифицирате източника:

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)

Леле какво беше това? Толкова много свикнах да виждам INSERT 0 1, че изходът ми в psql изглеждаше странен! Ах - вярно - създадох таблица с oids и просто отчаяно безсмислено използвах един (253967) идентификатор! Е - не напълно безсмислено (макар и отчаяно) - избраният връща два реда със същия ctid (0,1) - не е изненадващо - избирам от две таблици и след това добавям резултатите един към друг, така че шансът да имам същия ctid не е толкова ниско. Последното нещо, което трябва да спомена е, че мога отново да използвам типове идентификатори на обект, за да го покажа красиво:

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)

Аха! (казано с нарастваща интонация) - Така че това е начинът ясно да фиксирате източника на данни тук!

И накрая, още една много популярна и интересна употреба - дефиниране на кой ред е бил вмъкнат и кой е поставен:

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

Сега, когато имаме PK, мога да използвам директива 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)
Свързани ресурси ClusterControl за PostgreSQL Разбиране и четене на системния каталог на PostgreSQL Преглед на индексирането на база данни в PostgreSQL

Защо толкова щастлив? Защото мога да кажа (с известна конфиденциалност), че редът с xmax не е равен на нула, че е актуализиран. И не мислете, че е очевидно - изглежда така, само защото използвах unixtime за PK, така че изглежда наистина различно от едноцифрените стойности. Представете си, че правите такъв ON CONFLICT обрат на голям набор и няма логичен начин да определите коя стойност е имала конфликт и коя - не. xmax помогна на тонове DBA в трудни времена. И най-доброто описание на начина, по който работи, бих препоръчал тук – точно както бих препоръчал и тримата участници в дискусията (Абелисто, Ервин и Лоренц) да бъдат прочетени за други въпроси и отговори на маркера на postgres в SO.

Това е.

tableoid, xmax, xmin и ctid са добри приятели на всеки DBA. Да не обиждам cmax, cmin и oid - те също са също толкова добри приятели! Но това е достатъчно за малък преглед и сега искам да махна ръцете си от клавиатурата.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как мога да използвам UUID в SQLAlchemy?

  2. Извадете месеците от дата в PostgreSQL

  3. PostgreSQL конвенции за именуване

  4. Как да актуализирате множество колони в PostgreSQL

  5. Docker изчакайте да стартира postgresql