La versión 10 de PostgreSQL agregó la partición de tabla declarativa característica. En la versión 11 (actualmente en versión beta), puede combinar esto con envolturas de datos extranjeras , proporcionando un mecanismo para fragmentar de forma nativa sus tablas en varios servidores de PostgreSQL.
Particionamiento declarativo
Considere una tabla que almacene las temperaturas mínimas y máximas diarias de las ciudades para cada día:
CREATE TABLE temperatures (
at date,
city text,
mintemp integer,
maxtemp integer
);
La especificación de la tabla está intencionalmente desprovista de restricciones de columna y clave principal para simplificar las cosas; las agregaremos más adelante.
Es muy común encontrar que en muchas aplicaciones se accede con más frecuencia a los datos más recientes. Piense en el año financiero actual, este mes, la última hora, etc. A medida que crece nuestra tabla de "temperaturas", tiene sentido mover los datos antiguos a otra tabla, con la misma estructura. Podemos, por ejemplo, hacer esto:
CREATE TABLE temperatures_2017 (LIKE temperatures);
INSERT INTO temperatures_2017 SELECT * FROM temperatures WHERE
extract(year from at) = 2017;
DELETE FROM temperatures WHERE extract(year from at) = 2017;
para mover todas las entradas del año 2017 a otra tabla. Esto deja la tabla principal de "temperaturas" más pequeña y más rápida para que la aplicación funcione. en otra tabla.
Pero tener varias tablas distintas significa que el código de la aplicación ahora tiene que cambiar. Si tiene que acceder a datos más antiguos, por ejemplo, obtener las temperaturas mínimas y máximas anuales de una ciudad, ahora tiene que averiguar qué tablas están presentes en el esquema, consultar cada una de ellas y combinar los resultados de cada tabla. ¿Podemos hacer esto sin cambiar el código de la aplicación?
La partición lo hace posible. En PostgreSQL 10, puede crear la tabla de "temperaturas" de esta manera:
CREATE TABLE temperatures (
at date,
city text,
mintemp integer,
maxtemp integer
)
PARTITION BY RANGE (at);
Esto convierte a las "temperaturas" en una tabla maestra de particiones y le dice a PostgreSQL que vamos a crear varias tablas particionadas que almacenen datos que no se superpongan, cada una con un conjunto diferente de valores "at". La tabla maestra en sí misma no contiene ningún dato, pero la aplicación puede consultarla e insertarla, que ignora las particiones secundarias que contienen los datos reales.
Y aquí están nuestras particiones:
CREATE TABLE temperatures_2017
PARTITION OF temperatures
FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');
CREATE TABLE temperatures_2018
PARTITION OF temperatures
FOR VALUES FROM ('2018-01-01') TO ('2019-01-01');
Ahora tenemos dos tablas, una que almacenará datos para 2017 y otra para 2018. Tenga en cuenta que el valor "desde" es inclusivo, pero el valor "hasta" no lo es. Probémoslo:
temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-# VALUES ('2018-08-03', 'London', 63, 90);
INSERT 0 1
temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-# VALUES ('2017-08-03', 'London', 59, 70);
INSERT 0 1
temp=# SELECT * FROM temperatures;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2017-08-03 | London | 59 | 70
2018-08-03 | London | 63 | 90
(2 rows)
temp=# SELECT * FROM temperatures_2017;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2017-08-03 | London | 59 | 70
(1 row)
temp=# SELECT * FROM temperatures_2018;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2018-08-03 | London | 63 | 90
(1 row)
La "aplicación" puede insertarse y seleccionar desde la tabla principal, pero PostgreSQL enruta los datos reales a las tablas secundarias apropiadas. (¡Ah, y por cierto, esas temperaturas son reales!)
Índices y Restricciones
Los índices y las restricciones de tablas y columnas en realidad se definen en el nivel de la tabla de particiones, ya que es ahí donde residen los datos reales. Puede establecer estos durante la creación de la tabla de particiones:
CREATE TABLE temperatures_2017
PARTITION OF temperatures (
mintemp NOT NULL,
maxtemp NOT NULL,
CHECK (mintemp <= maxtemp),
PRIMARY KEY (at, city)
)
FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');
PostgreSQL 11 le permite definir índices en la tabla principal y creará índices en tablas de partición existentes y futuras. Lea más aquí.
Contenedor de datos externos
La funcionalidad de envoltorio de datos externos ha existido en Postgres durante algún tiempo. PostgreSQL le permite acceder a datos almacenados en otros servidores y sistemas utilizando este mecanismo. Lo que nos interesa es “postgres_fdw”, que es lo que nos permitirá acceder a un servidor Postgres desde otro.
“postgres_fdw” es una extensión presente en la distribución estándar, que se puede instalar con el comando regular CREATE EXTENSION:
CREATE EXTENSION postgres_fdw;
Supongamos que tiene otro servidor PostgreSQL "box2" con una base de datos llamada "box2db". Puede crear un "servidor extranjero" para esto:
CREATE SERVER box2 FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'box2', dbname 'box2db');
También asignemos nuestro usuario "alice" (el usuario con el que inició sesión) al usuario box2 "box2alice". Esto permite que "alice" sea "box2alice" al acceder a tablas remotas:
CREATE USER MAPPING FOR alice SERVER box2
OPTIONS (user 'box2alice');
Ahora puede acceder a tablas (también vistas, matviews, etc.) en box2. Primero, cree una tabla en box2 y luego una "tabla extranjera" en su servidor. La tabla externa no contiene ningún dato real, pero sirve como proxy para acceder a la tabla en box2.
-- on box2
CREATE TABLE foo (a int);
-- on your server
IMPORT FOREIGN SCHEMA public LIMIT TO (foo)
FROM SERVER box2 INTO public;
La mesa foránea en su servidor puede participar en transacciones de la misma manera que las mesas normales. Las aplicaciones no tienen que saber si las tablas con las que interactúa son locales o foráneas, aunque si su aplicación ejecuta un SELECT que podría extraer muchas filas de una tabla foránea, podría ralentizar las cosas. En Postgres 10, se realizaron mejoras para empujar hacia abajo las uniones y agregados al servidor remoto.
Combinar particionamiento y FDW
Y ahora viene la parte divertida:configurar particiones en servidores remotos.
Primero, creemos la tabla de particiones físicas en box2:
-- on box2
CREATE TABLE temperatures_2016 (
at date,
city text,
mintemp integer,
maxtemp integer
);
Y luego cree la partición en su servidor, como una tabla externa:
CREATE FOREIGN TABLE temperatures_2016
PARTITION OF temperatures
FOR VALUES FROM ('2016-01-01') TO ('2017-01-01')
SERVER box2;
Ahora puede insertar y consultar desde su propio servidor:
temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-# VALUES ('2016-08-03', 'London', 63, 73);
INSERT 0 1
temp=# SELECT * FROM temperatures ORDER BY at;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2016-08-03 | London | 63 | 73
2017-08-03 | London | 59 | 70
2018-08-03 | London | 63 | 90
(3 rows)
temp=# SELECT * FROM temperatures_2016;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2016-08-03 | London | 63 | 73
(1 row)
¡Ahí tienes! Poder insertar filas en una partición remota es una novedad en la versión 11. Con esta función, ahora puede fragmentar sus datos de forma lógica (particiones) y físicamente (FDW).
Gestión de datos
Los comandos como VACUUM y ANALYZE funcionan como cabría esperar con las tablas maestras de partición:todas las tablas secundarias locales están sujetas a VACUUM y ANALYZE. Las particiones se pueden separar, sus datos se manipulan sin la restricción de partición y luego se vuelven a unir. Las propias tablas secundarias de particiones se pueden particionar.
El movimiento de datos ("cambio de fragmentos") se puede hacer con instrucciones SQL normales (insertar, eliminar, copiar, etc.). Se pueden crear índices locales de partición y activadores.
Agregar redundancia a sus fragmentos se logra fácilmente con replicación lógica o de transmisión.