Las series temporales aparecen en cualquier sistema que registre métricas, logs, lecturas de IoT o eventos de aplicación. Una tabla típica tiene timestamp, identificador y valor, crece a millones o miles de millones de filas por mes, y se consulta por rangos de tiempo con agregaciones. PostgreSQL puro funciona, pero TimescaleDB añade primitivas específicas que multiplican el rendimiento y simplifican la operación.
Habilitar TimescaleDB
TimescaleDB es una extensión de PostgreSQL distribuida bajo licencia Apache 2 (community edition) y comercial (enterprise). Se instala como paquete del sistema y se habilita por base de datos:
CREATE EXTENSION IF NOT EXISTS timescaledb;
SELECT extversion FROM pg_extension WHERE extname = 'timescaledb';
Comprobación obligatoria: TimescaleDB requiere
shared_preload_libraries = 'timescaledb'enpostgresql.confy reiniciar el servidor antes de poder activar la extensión.
Hypertables: particionamiento automático por tiempo
El concepto central de TimescaleDB es la hypertable: una tabla que parece normal pero internamente se particiona automáticamente por intervalos temporales. Crear una métrica de IoT:
CREATE TABLE lecturas (
sensor_id INT NOT NULL,
instante TIMESTAMPTZ NOT NULL,
temperatura NUMERIC,
humedad NUMERIC,
presion NUMERIC
);
SELECT create_hypertable('lecturas', 'instante', chunk_time_interval => INTERVAL '1 day');
A partir de ese momento, cada INSERT se enruta automáticamente al chunk del día correspondiente. Las consultas con WHERE instante BETWEEN ... solo escanean los chunks relevantes mediante partition pruning.
-- Consultar 100 millones de filas filtradas por dia: solo escanea ese chunk
SELECT AVG(temperatura) FROM lecturas
WHERE sensor_id = 42
AND instante >= '2026-04-15'
AND instante < '2026-04-16';
El parámetro
chunk_time_intervales clave. Para datos de alta frecuencia (cada segundo) elige1 houro1 day. Para datos diarios1 monthpuede ser apropiado. Demasiados chunks pequeños degradan el planificador.
time_bucket: downsampling rápido
La función time_bucket redondea timestamps a intervalos específicos, equivalente a date_trunc pero más flexible (admite cualquier intervalo, no solo unidades estándar):
SELECT
time_bucket('5 minutes', instante) AS bucket,
sensor_id,
AVG(temperatura) AS temp_media,
MAX(temperatura) AS temp_max,
MIN(temperatura) AS temp_min
FROM lecturas
WHERE instante >= NOW() - INTERVAL '24 hours'
GROUP BY bucket, sensor_id
ORDER BY bucket DESC;
Esta consulta agrupa las lecturas en intervalos de 5 minutos. Es la base de los gráficos de Grafana y otros dashboards.
Continuous aggregates: vistas materializadas vivas
Las continuous aggregates son vistas materializadas especiales que TimescaleDB mantiene automáticamente a medida que llegan nuevos datos:
CREATE MATERIALIZED VIEW lecturas_horarias
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', instante) AS hora,
sensor_id,
AVG(temperatura) AS temp_media,
MAX(temperatura) AS temp_max,
MIN(temperatura) AS temp_min,
COUNT(*) AS num_lecturas
FROM lecturas
GROUP BY hora, sensor_id
WITH NO DATA;
-- Politica de refresco: actualizar cada 30 min los datos del ultimo dia,
-- conservando como historico todo lo anterior a 1 hora
SELECT add_continuous_aggregate_policy('lecturas_horarias',
start_offset => INTERVAL '1 day',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '30 minutes'
);
Una vez configurada, las consultas pueden ir directamente a lecturas_horarias con la garantía de que está actualizada. El almacenamiento incremental evita reescribir agregaciones de periodos pasados.
Las continuous aggregates son la herramienta para crear dashboards rápidos sobre datasets enormes. Un panel de Grafana que consulte 6 meses agregados por hora se sirve desde la vista, no desde los miles de millones de filas crudas.
Compresión nativa
TimescaleDB añade compresión columnar para chunks antiguos, con ratios típicos del 90 al 95 %:
ALTER TABLE lecturas SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'sensor_id'
);
-- Politica que comprime chunks de mas de 7 dias automaticamente
SELECT add_compression_policy('lecturas', INTERVAL '7 days');
El parámetro compress_segmentby indica por qué columna agrupar: las consultas que filtren por esa columna serán más rápidas que las que no. Las filas comprimidas siguen siendo consultables; sólo las modificaciones requieren descomprimir el chunk.
-- Ver el estado de compresion
SELECT chunk_name, before_compression_total_bytes, after_compression_total_bytes
FROM timescaledb_information.chunks
WHERE hypertable_name = 'lecturas';
Políticas de retención
Para datos que pierden valor con el tiempo, una política de retención elimina automáticamente chunks antiguos:
SELECT add_retention_policy('lecturas', INTERVAL '1 year');
Esto borra cada noche todos los chunks cuyos datos son anteriores a 1 año. Las políticas de continuous aggregate, retención y compresión combinadas implementan un ciclo de vida completo: datos calientes en bruto, datos templados comprimidos, datos fríos agregados, datos viejos eliminados.
Funciones específicas para series
TimescaleDB añade funciones útiles para análisis temporal que SQL puro no ofrece:
first(value, time)ylast(value, time): primer y último valor del grupo según el orden temporal.time_bucket_gapfill: comotime_bucketpero rellena huecos conNULL, útil para gráficos sin saltos.locf(last observation carried forward): rellena huecos con el último valor conocido.interpolate: interpola linealmente entre dos puntos conocidos.
SELECT
time_bucket_gapfill('5 minutes', instante) AS bucket,
sensor_id,
locf(AVG(temperatura)) AS temp_media,
interpolate(AVG(humedad)) AS humedad_interp
FROM lecturas
WHERE instante >= NOW() - INTERVAL '1 hour'
AND sensor_id = 42
GROUP BY bucket, sensor_id;
Esta consulta produce un punto cada 5 minutos durante la última hora, rellenando huecos con
locfpara temperatura y con interpolación para humedad. Ideal para alimentar gráficos sin necesidad de procesado en cliente.
Caso típico: monitorización IoT
Un sistema de monitorización completo de sensores IoT podría tener:
-- 1. Tabla cruda hipertable
CREATE TABLE lecturas (
sensor_id INT,
instante TIMESTAMPTZ NOT NULL,
valor NUMERIC
);
SELECT create_hypertable('lecturas', 'instante', chunk_time_interval => INTERVAL '1 day');
-- 2. Vista agregada por minuto
CREATE MATERIALIZED VIEW lecturas_minuto WITH (timescaledb.continuous) AS
SELECT time_bucket('1 minute', instante) AS minuto,
sensor_id,
AVG(valor) AS valor_medio
FROM lecturas
GROUP BY minuto, sensor_id;
-- 3. Politica de mantenimiento de la vista
SELECT add_continuous_aggregate_policy('lecturas_minuto',
start_offset => INTERVAL '1 hour',
end_offset => INTERVAL '1 minute',
schedule_interval => INTERVAL '1 minute');
-- 4. Compresion de datos crudos pasados 24h
SELECT add_compression_policy('lecturas', INTERVAL '1 day');
-- 5. Borrado de datos crudos pasados 90 dias
SELECT add_retention_policy('lecturas', INTERVAL '90 days');
Con este esquema:
- Los datos crudos se guardan 90 días, comprimidos a partir de las 24 horas.
- Las agregaciones por minuto se guardan indefinidamente.
- Las dashboards consultan la vista, son rápidos.
flowchart TB
A[Sensores IoT] -->|INSERT cada segundo| B[Hypertable lecturas]
B --> C{Edad del chunk}
C -->|Menor de 24h| D[Sin comprimir]
C -->|24h a 90 dias| E[Comprimido]
C -->|Mas de 90 dias| F[Eliminado]
B --> G[Continuous aggregate<br/>lecturas_minuto]
G --> H[Dashboard Grafana]
Cuándo usar TimescaleDB y cuándo no
TimescaleDB es la elección correcta cuando:
- Trabajas con series temporales de gran volumen.
- Necesitas agregaciones rápidas sobre rangos amplios.
- Quieres compresión transparente y políticas de retención.
- Ya usas PostgreSQL y prefieres no operar otra base de datos.
Es excesivo cuando:
- Las tablas tienen pocas filas y caben en RAM.
- No hay un patrón temporal claro en las consultas.
- La carga es OLTP estándar sin componente analítico.
En entornos donde InfluxDB, ClickHouse o Druid serían candidatos, TimescaleDB suele ser una alternativa igual de rápida con la ventaja de mantener todo el ecosistema PostgreSQL: triggers, foreign keys, transacciones y librerías de cliente.
TimescaleDB convierte a PostgreSQL en la base de datos de series temporales más versátil del ecosistema open source. Para equipos que quieren consolidar su stack de datos sin renunciar a rendimiento ni a funcionalidades específicas de series, es una de las decisiones técnicas con mayor retorno de inversión.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SQL es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de SQL
Explora más contenido relacionado con SQL y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Habilitar TimescaleDB y convertir tablas en hypertables con create_hypertable. Insertar y consultar series temporales con time_bucket para downsampling. Crear continuous aggregates que se mantienen automáticamente. Aplicar compresión y políticas de retención automáticas. Conocer las funciones específicas first, last, locf y time_bucket_gapfill.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje