PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Entendendo as colunas do sistema no PostgreSQL


Então você se senta com as mãos sobre um teclado e pensa “que diversão eu posso ter para tornar minha vida ainda mais curiosa? ..” Bem - crie uma mesa, é claro!
vao=# create table nocol();
CREATE TABLE
vao=# select * from nocol;
--
(0 rows)

Qual é a graça de uma tabela sem dados?. Absolutamente nenhum! Mas eu posso corrigi-lo facilmente:
vao=# insert into nocol default values;
INSERT 0 1

Parece estranho e bastante estúpido ter uma tabela sem colunas e uma linha. Sem mencionar que não está claro quais “valores padrão” foram inseridos… Bem - lendo algumas linhas de documentos revela que “Todas as colunas serão preenchidas com seus valores padrão .” No entanto, não tenho colunas! Bem - eu certamente tenho alguns:
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)

Portanto, esses seis definitivamente não são os zumbis ALTER TABLE DROP COLUMN porque attisdropped é falso. Também vejo que o nome do tipo dessas colunas termina com “id”. Ler a seção inferior de Tipos de identificador de objeto dará a ideia. Outra observação engraçada é - o -2 está faltando! Eu me pergunto onde eu poderia tê-lo perdido - acabei de criar uma tabela, afinal! Hm, qual identificador de objeto está faltando na minha tabela? Por definição quero dizer. Eu tenho ids de tupla, comando e xact. Bem, a menos que algum "identificador global sobre todo o banco de dados", como oid? .. A verificação é fácil - vou criar uma tabela com 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)

Voilá! Portanto, o -2 ausente está realmente ausente e nós gostamos. Gastar oids para linhas de dados usadas seria uma má ideia, então continuarei brincando com uma tabela sem OIDS.

O que eu tenho? Eu tenho 6 atributos depois de criar “sem tabela de colunas” com (oids=false). Devo usar colunas do sistema? Se sim, por que eles estão meio escondidos? Bem - eu diria que eles não são tão amplamente divulgados, porque o uso não é intuitivo e o comportamento pode mudar no futuro. Por exemplo, depois de ver o id da tupla (ctid), alguns podem pensar "ah - isso é uma espécie de PK interno" (e é):
vao=# select ctid from nocol;
 ctid  
-------
 (0,1)
(1 row)

Os primeiros dígitos (zero) representam o número da página e os segundos (um) representam o número da tupla. Eles são sequenciais:
vao=# insert into nocol default values;
INSERT 0 1
vao=# select ctid from nocol;
 ctid  
-------
 (0,1)
 (0,2)
(2 rows)

Mas essa sequência não ajudará você a definir nem mesmo qual linha chegou depois da qual:
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)

Aqui eu adicionei uma coluna (para identificar minhas linhas) e a preenchi com o número inicial da tupla (lembre-se de que ambas as linhas foram movidas fisicamente)
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)

Ah! (disse com entonação crescente) - aqui eu deletei uma das minhas linhas, soltei o vácuo na mesa pobre e inseri uma nova linha. O resultado - a linha adicionada mais tarde está na primeira tupla da primeira página, porque o Postgres sabiamente decidiu salvar o espaço e reutilizar o espaço liberado.

Portanto, a ideia de usar ctid para obter a sequência de linhas introduzidas parece ruim. Até certo nível - se você trabalhar em uma transação, a sequência permanece - as linhas recém-afetadas na mesma tabela terão ctid "maior". Claro que após o vácuo (autovacuum) ou se você tiver a sorte de ter atualizações HOT mais cedo ou apenas as lacunas liberadas serão reutilizadas - quebrando a ordem sequencial. Mas não tenha medo - havia seis atributos ocultos, não um!
vao=# select i, ctid, xmin from nocol;
 i | ctid  | xmin  
---+-------+-------
   | (0,1) | 26211
 2 | (0,4) | 26209
(2 rows)

Se eu verificar o xmin, verei que o ID da transação que introduziu a última linha inserida é (+2) maior (+1 foi a linha excluída). Portanto, para o identificador de linha sequencial, posso usar um atributo totalmente diferente! Claro que não é tão simples, caso contrário, esse uso seria incentivado. A coluna xmin antes de 9.4 foi na verdade sobrescrita para proteger do xid wraparound. Por que tão complicado? O MVCC no Postgres é muito inteligente e os métodos em torno dele melhoram com o tempo. Claro que traz complexidade. Infelizmente. Algumas pessoas até querem evitar colunas do sistema. Ai duplo. Porque as colunas do sistema são legais e bem documentadas. O atributo muito superior (lembre-se que eu pulo oids) é tableoid:
vao=# select i, tableoid from nocol;
 i | tableoid 
---+----------
   |   253952
 2 |   253952
(2 rows)
Baixe o whitepaper hoje PostgreSQL Management &Automation with ClusterControlSaiba o que você precisa saber para implantar, monitorar, gerenciar e dimensionar o PostgreSQLBaixe o whitepaper
Parece inútil ter o MESMO valor em todas as linhas - não é? E ainda há um tempo atrás era um atributo muito popular - quando todos estávamos construindo particionamento usando regras e tabelas herdadas. Como você depuraria de qual tabela a linha está vindo se não for com tableoid? Então quando você usa regras, views (mesmas regras) ou UNION o atributo tableoid te ajuda a identificar a fonte:
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)

Nossa o que foi isso? Eu me acostumei tanto a ver INSERT 0 1 que minha saída do psql ficou estranha! Ah - é verdade - eu criei uma tabela com oids e usei desesperadamente um (253967) identificador! Bem - não completamente sem sentido (embora desesperadamente) - o select retorna duas linhas com o mesmo ctid (0,1) - não é surpreendente - estou selecionando de duas tabelas e adicionando resultados um ao outro, então a chance de ter o mesmo ctid não é tão baixo. A última coisa a mencionar é que posso usar novamente os tipos de identificador de objeto para mostrá-lo bonito:
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)

Ah! (disse com entonação crescente) - Então essa é a maneira de fixar claramente a fonte de dados aqui!

Finalmente, outro uso muito popular e interessante - definindo qual linha foi inserida e qual upsert:
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

Agora que temos um PK, posso usar a diretiva 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)
Recursos relacionados ClusterControl para PostgreSQL Compreendendo e lendo o catálogo do sistema PostgreSQL Uma visão geral da indexação de banco de dados no PostgreSQL
Porque tão feliz? Pois posso dizer (com alguma confidencialidade) aquela linha com xmax diferente de zero que foi atualizada. E não pense que é óbvio - parece tão só porque eu usei unixtime para PK, então parece muito diferente de valores de um dígito. Imagine que você faça uma reviravolta NO CONFLITO em um grande set e não há uma maneira lógica de identificar qual valor teve conflito e qual - não. xmax ajudou toneladas de DBAs em tempos difíceis. E a melhor descrição de como funciona eu recomendaria aqui - assim como eu recomendaria que todos os três participantes da discussão (Abelisto, Erwin e Laurenz) fossem lidos em outras perguntas e respostas da tag postgres no SO.

É isso.

tableoid, xmax, xmin e ctid são bons amigos de qualquer DBA. Para não insultar cmax, cmin e oid - eles também são bons amigos! Mas isso é suficiente para uma pequena revisão e quero tirar minhas mãos do teclado agora.