Conceptos básicos de series temporales
Las cargas de trabajo de series temporales en PostgreSQL se benefician del particionamiento por rangos en marcas de tiempo, los índices BRIN para escaneos de solo adición y las políticas de retención que desvinculan o eliminan particiones antiguas. El particionamiento nativo cubre muchos casos de uso de métricas antes de TimescaleDB.
Receta
CREATE TABLE metrics (
device_id int NOT NULL,
ts timestamptz NOT NULL,
value double precision NOT NULL
) PARTITION BY RANGE (ts);
CREATE TABLE metrics_2026_07 PARTITION OF metrics
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');
CREATE INDEX metrics_2026_07_device_ts ON metrics_2026_07 (device_id, ts DESC);
-- BRIN para particiones grandes de solo adición (complemento opcional)
CREATE INDEX metrics_2026_07_ts_brin ON metrics_2026_07 USING brin (ts);Cuándo usar esto: Inserciones de alto volumen con consultas limitadas por tiempo (paneles, IoT, métricas de auditoría) donde los escaneos secuenciales en un único heap gigante están fallando.
Ejemplo de trabajo
INSERT INTO metrics (device_id, ts, value)
SELECT (random() * 100)::int,
timestamptz '2026-07-01' + (g * interval '1 minute'),
random()
FROM generate_series(0, 10000) g;
EXPLAIN (ANALYZE, BUFFERS)
SELECT date_trunc('hour', ts) AS hour,
avg(value) AS avg_value
FROM metrics
WHERE ts >= timestamptz '2026-07-08'
AND ts < timestamptz '2026-07-09'
AND device_id = 42
GROUP BY 1
ORDER BY 1;
-- Retención: eliminar la partición del mes anterior
DROP TABLE IF EXISTS metrics_2025_01;Lo que esto demuestra:
- La poda de particiones limita los datos escaneados a la tabla hija de julio de 2026.
- El índice compuesto
(device_id, ts DESC)soporta filtros + rangos de tiempo. - BRIN opcional para particiones muy grandes con heap correlacionado por tiempo.
DROP TABLEen una partición es una retención rápida frente aDELETEde millones de filas.
Profundización
Estrategia de particionamiento
| Grano | Cuándo | Acción de retención |
|---|---|---|
| Mensual | Métricas generales | DROP TABLE o desvincular a almacenamiento en frío |
| Diario | Volumen muy alto | Crear automáticamente mediante pg_partman o cron |
| Anual | Bajo volumen, archivo a largo plazo | Desvincular a tabla externa de almacenamiento de objetos |
-- Crear la partición del próximo mes (automatizar en producción)
CREATE TABLE metrics_2026_08 PARTITION OF metrics
FOR VALUES FROM ('2026-08-01') TO ('2026-09-01');BRIN en marcas de tiempo
BRIN sobresale cuando las filas están físicamente correlacionadas con ts (solo adición). Tamaño de índice diminuto frente a B-tree.
CREATE INDEX ON metrics_2026_07 USING brin (ts) WITH (pages_per_range = 128);Combine BRIN con la poda de particiones: cada partición mensual obtiene su propio BRIN.
B-tree vs BRIN
| Índice | Costo de inserción | Escaneo de rango por tiempo | Mejor para |
|---|---|---|---|
| B-tree (ts) | Más alto | Preciso | Lectura/escritura mixta, dispositivo+tiempo |
| BRIN (ts) | Muy bajo | Rangos de bloques aproximados | Inundación de solo adición |
A menudo se usan ambos: btree en (device_id, ts) para puntos/rangos por dispositivo, BRIN en ts para agregaciones amplias si es necesario.
Disciplina de Timestamptz
-- Siempre timestamptz para series (nunca timestamp sin zona horaria)
ALTER TABLE metrics ALTER COLUMN ts TYPE timestamptz USING ts AT TIME ZONE 'UTC';Almacenar en UTC; convertir en la aplicación para su visualización.
Errores comunes
- Partición predeterminada de captura - Todo va a DEFAULT, la poda falla. Solución: Monitorear los límites de las particiones; alertar antes de que DEFAULT se llene.
- Retención con DELETE - Las eliminaciones de millones de filas inflan la tabla y WAL. Solución:
DROP PARTITIONoDETACH+ archivar. - Partición futura faltante - Las inserciones fallan en el límite del mes. Solución: Automatizar la creación de particiones con 2 meses de antelación.
- timestamp sin zona horaria - Errores de DST en agregaciones. Solución: Solo
timestamptz; ver el artículo sobre escenarios de defectos. - BRIN con orden de inserción aleatorio - BRIN inútil si el heap no está correlacionado. Solución: Patrón de carga de solo adición o omitir BRIN.
- Índice global en el padre - No se puede crear un índice único en todas las particiones sin incluir la clave de partición (reglas de PG 11+). Solución: Incluir
tsen claves únicas.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Tablas Híper de TimescaleDB | Compresión, agregados continuos aprobados | La extensión no está en la lista de permitidos |
| Tabla única sin particionar | < 50M filas, operaciones simples | Las eliminaciones de retención ahogan el vacuum |
| Descarga a ClickHouse/BigQuery | Escala OLAP (ver artículo ADR) | Necesita uniones transaccionales de estado del dispositivo |
| Solo tablas de resumen | Paneles fijos | Exploración ad hoc de eventos sin procesar |
Preguntas frecuentes
¿Cuándo particionar?
Cuando las eliminaciones de retención o las consultas en rangos de tiempo exceden el SLO en un heap único (a menudo 50 GB+ o millones/día).¿pg_partman?
Extensión popular para el mantenimiento de particiones; requiere aprobación de la lista de permitidos.¿BRIN pages_per_range?
Menor = más preciso, índice más grande; ajustar con EXPLAIN en escaneos de rango.¿Métricas únicas?
UNIQUE (device_id, ts) debe incluir la clave de partición ts en las tablas particionadas de PG.¿Rellenar datos antiguos?
Crear partición para el rango histórico antes de COPY.¿Réplicas de lectura?
La DDL de partición se replica; planificar la creación antes del cambio de mes en el primario.¿Poolers de conexión?
pgbouncer está bien para ráfagas de INSERT; vigilar los tiempos de espera de sentencias en agregaciones grandes.¿Métricas JSONB?
Extraer claves "calientes" a columnas; BRIN en ts todavía ayuda a escanear particiones.¿Rellenar huecos?
generate_series en SQL para gráficos; no es un problema de almacenamiento.¿Próximo paso?
Artículo de TimescaleDB si la extensión está aprobada; de lo contrario, automatización de particiones nativas.Relacionado
- Conceptos básicos de particionamiento - particionamiento nativo
- BRIN - índices de rango de bloques
- TimescaleDB - extensión hypertables
- Mejores prácticas para series temporales - retención como código
- ADR de descarga OLAP - cuándo abandonar Postgres
Versiones de la pila: Esta página se escribió para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PostGIS 3.5+, pgbouncer 1.x y Patroni 3.x.