sql >> Base de Datos >  >> RDS >> PostgreSQL

Comprender las columnas del sistema en PostgreSQL

Así que te sientas con las manos sobre un teclado y piensas "¿qué diversión puedo tener para hacer que mi vida sea aún más curiosa?" Bueno, ¡crea una tabla, por supuesto!

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

¿Qué tiene de divertido una tabla sin datos?... ¡Absolutamente ninguno! Pero puedo arreglarlo fácilmente:

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

Parece extraño y bastante estúpido tener una tabla sin columnas y con una sola fila. Sin mencionar que no está claro qué "valores predeterminados" se insertaron... Bueno, leer algunas líneas de los documentos revela que "Todas las columnas se completarán con sus valores predeterminados .” ¡Sin embargo, no tengo columnas! Bueno, seguramente tengo algunos:

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)

Así que estos seis definitivamente no son los zombis ALTER TABLE DROP COLUMN porque atisdropped es falso. También veo que el nombre de tipo de esas columnas termina con "id". Leer la sección inferior de Tipos de identificadores de objetos le dará una idea. Otra observación divertida es:¡falta el -2! Me pregunto dónde podría haberlo perdido. ¡Después de todo, acabo de crear una tabla! Hm, ¿qué identificador de objeto falta en mi tabla? Por definición quiero decir. Tengo identificadores de tupla, comando y xact. Bueno, a menos que algún "identificador global sobre toda la base de datos", como oid?... La verificación es fácil:crearé una tabla con 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! Así que falta el -2 y nos gusta. Gastar oids en filas de datos usadas sería una mala idea, así que seguiré jugando con una mesa sin OIDS.

¿Lo que tengo? Tengo 6 atributos después de crear "tabla sin columnas" con (oids=false). ¿Debo usar columnas del sistema? Si es así, ¿por qué están un poco escondidos? Bueno, supongo que no se anuncian tan ampliamente, porque el uso no es intuitivo y el comportamiento puede cambiar en el futuro. Por ejemplo, después de ver la identificación de tupla (ctid), algunos podrían pensar "ah, esto es una especie de PK interno" (y lo es):

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

Los primeros dígitos (cero) representan el número de página y el segundo (uno) representa el número de tupla. Son secuenciales:

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

Pero esta secuencia no te ayudará a definir qué fila llegó después de cuál:

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)

Aquí agregué una columna (para identificar mis filas) y la llené con el número de tupla inicial (tenga en cuenta que ambas filas se movieron físicamente)

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)

¡Ajá! (dicho con entonación ascendente) - aquí borré una de mis filas, dejé salir el vacío sobre la pobre mesa e inserté una nueva fila. El resultado:la última fila agregada está en la primera tupla de la primera página, porque Postgres sabiamente decidió guardar el espacio y reutilizar el espacio liberado.

Por lo tanto, la idea de usar ctid para introducir la secuencia de filas parece mala. Hasta cierto nivel, si trabaja en una transacción, la secuencia permanece, las filas recién afectadas en la misma tabla tendrán un ctid "más grande". Por supuesto, después del vacío (vacío automático) o si tiene la suerte de tener actualizaciones HOT antes o simplemente se reutilizarán los espacios vacíos, rompiendo el orden secuencial. Pero no temas:¡había seis atributos ocultos, no uno!

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

Si verifico el xmin, veré que la identificación de la transacción que introdujo la última fila insertada es (+2) más alta (+1 fue la fila eliminada). Entonces, para el identificador de fila secuencial, ¡podría usar un atributo totalmente diferente! Por supuesto, no es tan simple, de lo contrario, se alentaría dicho uso. La columna xmin antes de 9.4 en realidad se sobrescribió para protegerla del ajuste xid. ¿Por qué tan complicado? El MVCC en Postgres es muy inteligente y los métodos a su alrededor mejoran con el tiempo. Por supuesto que trae complejidad. Pobre de mí. Algunas personas incluso quieren evitar las columnas del sistema. Doble ay. Porque las columnas del sistema son geniales y están bien documentadas. El atributo superior (recuerde que me salteo los oids) es tableoid:

vao=# select i, tableoid from nocol;
 i | tableoid 
---+----------
   |   253952
 2 |   253952
(2 rows)
Descargue el documento técnico hoy Gestión y automatización de PostgreSQL con ClusterControl Obtenga información sobre lo que necesita saber para implementar, monitorear, administrar y escalar PostgreSQLDescargar el documento técnico

Parece inútil tener el MISMO valor en cada fila, ¿no? Y, sin embargo, hace un tiempo era un atributo muy popular, cuando todos estábamos construyendo particiones usando reglas y tablas heredadas. ¿Cómo depuraría de qué tabla proviene la fila si no fuera con tableoid? Entonces, cuando usa reglas, vistas (mismas reglas) o UNION, el atributo tableoid lo ayuda a identificar la fuente:

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)

Vaya que fue eso? ¡Me he acostumbrado tanto a ver INSERT 0 1 que mi salida de psql se ve rara! Ah, cierto, ¡creé una tabla con oids y usé desesperadamente sin sentido un identificador (253967)! Bueno, no completamente sin sentido (aunque desesperadamente), la selección devuelve dos filas con el mismo ctid (0,1), no es sorprendente, estoy seleccionando de dos tablas y luego agregando resultados uno a otro, por lo que la posibilidad de tener el mismo ctid no es tan bajo. Lo último que hay que mencionar es que puedo volver a usar tipos de identificadores de objetos para mostrarlo 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)

¡Ajá! (dicho con entonación ascendente) - ¡Así que esa es la manera de fijar claramente la fuente de datos aquí!

Finalmente, otro uso muy popular e interesante:definir qué fila se insertó y cuál se alteró:

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

Ahora que tenemos un PK, puedo usar la directiva 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 Comprender y leer el catálogo del sistema de PostgreSQL Una descripción general de la indexación de bases de datos en PostgreSQL

¿Porque tan feliz? Porque puedo decir (con cierta confidencialidad) que la fila con xmax no es igual a cero que se actualizó. Y no creas que es obvio:lo parece solo porque usé unixtime para PK, por lo que se ve muy diferente a los valores de un dígito. Imagínese que hace ese giro EN CONFLICTO en un conjunto grande y no hay una forma lógica de identificar qué valor tuvo conflicto y cuál no. xmax ayudó a toneladas de administradores de bases de datos en tiempos difíciles. Y la mejor descripción de cómo funciona la recomendaría aquí, al igual que recomendaría que los tres participantes de la discusión (Abelisto, Erwin y Laurenz) se leyeran en otras preguntas y respuestas de etiquetas de postgres en SO.

Eso es todo.

tableoid, xmax, xmin y ctid son buenos amigos de cualquier DBA. Sin insultar a cmax, cmin y oid, ¡también son buenos amigos! Pero esto es suficiente para una pequeña revisión y quiero quitar mis manos del teclado ahora.