Conceptos básicos de partición
8 ejemplos para empezar con la partición: 5 básicos y 3 intermedios.
Prerrequisitos
- Clúster de PostgreSQL 18.4 con privilegios DDL.
- Comprensión de los filtros de tus consultas (
WHEREclauses) y la política de retención.
Ejemplos básicos
1. Partición por rango según el mes
Divide los eventos de series temporales por mes calendario: el patrón más común.
CREATE TABLE events (
event_id bigint GENERATED ALWAYS AS IDENTITY,
occurred_at timestamptz NOT NULL,
payload jsonb NOT NULL,
PRIMARY KEY (event_id, occurred_at)
) PARTITION BY RANGE (occurred_at);
CREATE TABLE events_2026_01 PARTITION OF events
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE events_2026_02 PARTITION OF events
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');- La tabla padre no almacena filas; los datos residen en las particiones hijas.
- La clave de partición (
occurred_at) debe aparecer en las restriccionesPRIMARY KEYyUNIQUE. - Los límites del rango son semiabiertos:
FROMinclusivo,TOexclusivo.
Relacionado: DDL de partición declarativa - configuración padre/hijo
2. Partición por lista según la región
Dirige las filas a particiones regionales cuando las consultas filtran por un conjunto discreto.
CREATE TABLE orders (
order_id bigint GENERATED ALWAYS AS IDENTITY,
region text NOT NULL,
amount numeric(12, 2) NOT NULL,
PRIMARY KEY (order_id, region)
) PARTITION BY LIST (region);
CREATE TABLE orders_us PARTITION OF orders FOR VALUES IN ('us');
CREATE TABLE orders_eu PARTITION OF orders FOR VALUES IN ('eu', 'uk');
CREATE TABLE orders_apac PARTITION OF orders FOR VALUES IN ('apac');- La partición por lista se adapta a claves tipo enumeración de baja cardinalidad.
- Agrupa regiones de bajo volumen en una sola partición para evitar hijos vacíos.
- Las consultas con
WHERE region = 'eu'se podan aorders_eu.
Relacionado: Poda de particiones - demuestra que los planes omiten particiones
3. Partición por hash para una distribución uniforme
Distribuye las filas cuando no existe un filtro natural de rango o lista.
CREATE TABLE sessions (
session_id uuid NOT NULL,
tenant_id bigint NOT NULL,
created_at timestamptz NOT NULL,
PRIMARY KEY (session_id, tenant_id)
) PARTITION BY HASH (tenant_id);
CREATE TABLE sessions_p0 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE sessions_p1 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE sessions_p2 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE sessions_p3 PARTITION OF sessions FOR VALUES WITH (MODULUS 4, REMAINDER 3);- El hash distribuye la carga de escritura en un número fijo de particiones.
- El módulo debe coincidir con el número total de particiones hash.
- La poda requiere
WHERE tenant_id = ?(o listaIN), no escaneos de rango en todos los tenants.
4. Partición predeterminada de "catch-all"
Captura las filas que no coinciden con las particiones explícitas durante la implementación.
CREATE TABLE events_default PARTITION OF events DEFAULT;
INSERT INTO events (occurred_at, payload)
VALUES ('2027-05-15', '{"type": "signup"}');
-- se almacena en events_default hasta que añadas events_2027_05- La partición predeterminada evita fallos de inserción para claves no mapeadas.
- Supervisa el crecimiento de la partición predeterminada: deshabilita la poda óptima para esas filas.
- Reemplázala con particiones explícitas y migra las filas fuera de la predeterminada.
Relacionado: Trampas de la partición - trampas de la partición predeterminada
5. Índices en tablas particionadas
Los índices en la tabla padre se propagan a las hijas existentes y futuras.
CREATE INDEX events_occurred_at_idx ON events (occurred_at);
CREATE INDEX events_payload_gin_idx ON events USING gin (payload);- Crea índices en la tabla padre para DDL consistente en todas las particiones.
- Cada hija almacena sus propios archivos de índice físicos.
- Alinea los índices con predicados amigables para la poda en
WHERE.
Ejemplos intermedios
6. Adjuntar partición histórica precargada
Carga datos sin conexión y luego adjúntala como partición para un cambio rápido.
CREATE TABLE events_2025_12 (
event_id bigint GENERATED BY DEFAULT AS IDENTITY,
occurred_at timestamptz NOT NULL,
payload jsonb NOT NULL
);
COPY events_2025_12 (occurred_at, payload) FROM '/tmp/events_2025_12.csv' CSV;
ALTER TABLE events ATTACH PARTITION events_2025_12
FOR VALUES FROM ('2025-12-01') TO ('2026-01-01');ATTACHvalida los límites contra la restricción de la tabla padre.- Precrea restricciones
CHECKen la tabla independiente antes de adjuntarla para una validación más rápida. - Útil para rellenos retroactivos e importaciones de archivo.
Relacionado: Retención y desvinculación - desvincula y elimina datos antiguos
7. Demostrar la poda con EXPLAIN
Confirma que el planificador escanea solo las particiones relevantes.
EXPLAIN (COSTS OFF)
SELECT COUNT(*) FROM events
WHERE occurred_at >= '2026-01-15' AND occurred_at < '2026-02-01';- Busca
Partitions pruned: No nodos de escaneo de hijas solo paraevents_2026_01. - Sin predicados amigables para la poda, se escanean todas las particiones.
- Ejecuta después de la actualización de estadísticas:
ANALYZE events;
Relacionado: Poda de particiones - reglas de poda
8. Desvincular partición antigua para retención
Elimina un mes de la tabla activa sin eliminar fila por fila.
ALTER TABLE events DETACH PARTITION events_2025_01;
-- Opcional: archiva la tabla desvinculada y luego elimínala
ALTER TABLE events_2025_01 RENAME TO events_archive_2025_01;DETACHconvierte la hija en una tabla independiente instantáneamente.- Elimina o archiva la tabla desvinculada para recuperar espacio.
- Mucho más rápido que
DELETE FROM events WHERE occurred_at < ...en tablas de miles de millones de filas.
Relacionado: Retención y desvinculación - flujos de trabajo de archivo
Versiones de la pila: Esta página se escribió para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x y PostGIS 3.5+.