Sistema de almacenamiento de Series Temporales (TSDB)

Cuando diseñé la arquitectura de métricas para Deeditt, me enfrenté al desafío de procesar y almacenar grandes volúmenes de eventos temporales de forma eficiente y a bajo coste operativo. Cada interacción de usuario —descubrimientos, impresiones, reacciones, conversiones y tiempo de permanencia— genera un punto de datos que se debe retener primero en memoria (Redis), luego en un sistema de series temporales (TSDB) con retención de 15+n días, y finalmente consolidar históricamente en Postgres. Elegir la tecnología adecuada para el TSDB fue un punto crítico: necesitaba un motor capaz de soportar ingestas de hasta 100 000 puntos por segundo, ofrecer agregaciones nativas y políticas de retención integradas, pero consumiendo muy pocos recursos.
Inicialmente empecé con Redis + Postgres y funcionaba perfectamente, pero ya hacía mucho tiempo que quería probar algún motor de TSDB y ver si el rendimiento mejoraba a la vez que distribuía los recursos de una forma diferente y más equilibrada, pues Postgres estaba recibiendo mucha carga.
Y así fue como llegué a evaluar InfluxDB y TimescaleDB, qué factores técnicos me llevaron a quedarme con InfluxDB y cómo encaja en mi flujo concreto de trabajo. Explicaré datos de rendimiento, comparaciones, pros y contras, y la justificación de por qué InfluxDB, aun con retención limitada, fue la solución más ligera y sencilla de operar.
Empecemos evaluando la estructura de datos
Mi aplicación genera eventos de tracking a alta frecuencia. Un punto de datos es un conjunto de campos como:
object_id
(ID de la publicación)user_id
(ID del visitante)impressions
(conteo incremental)unique_visitors
(contador HyperLogLog)dwell_time
(tiempo en segundos)
Y otras métricas de engagement y conversión más. Estos eventos llegan desde varias fuentes (API, colas) y, en horas pico, podemos estar recibiendo decenas de miles de eventos por segundo. Para no saturar la base de datos relacional y poder responder consultas de ventana de tiempo (última hora, último día, últimos 30 días) con baja latencia, diseñé un pipeline:
- Redis: buffer en caliente con TTL de 24 h.
- Flush inmediato (cada 5 minutos) de Redis a TSDB.
- TSDB retiene los datos de los últimos 30 días.
- Flush semanal de TSDB a Postgres, donde guardamos un histórico permanente en la tabla unificada
entity_stats
. - API de consulta: combina histórico (+ 30 días) desde Postgres y reciente (< 30 días) desde TSDB, con caching adicional en Redis (TTL = 5 min).
El reto principal es elegir un TSDB que:
- Soporte ingestas sostenidas de hasta 100 000 puntos por segundo sin caídas.
- Ofrezca agregaciones por ventana (sumas, promedios, derivadas) internamente.
- Gestione retención y downsampling sin mantenimiento manual.
- Consuma poca CPU y RAM —teniendo ya un Redis y un Postgres en producción— y que sea sencillo de operar.
Espera, ¿dijiste “tasa de ingesta”?
Cuando digo que un TSDB debe manejar 100 000 puntos por segundo, me refiero a que, sin cuellos de botella, debería aceptar, almacenar e indexar esa cantidad de operaciones de escritura por segundo. Un “punto” incluye múltiples tags y fields, y el sistema debe volcarlos a disco (o memoria) rápidamente, sin bloquear lecturas o saturar I/O. Si el TSDB no está optimizado para series temporales, a esos volúmenes Postgres suele colapsar y Redis no persiste ni indexa adecuadamente por tiempo.
Candidatos a evaluar
- InfluxDB (TSM, Go).
- TimescaleDB (extensión de Postgres en C).
Métricas clave recopiladas
Métrica | InfluxDB | TimescaleDB |
---|---|---|
Footprint RAM mínimo | ~200–500 MB | ≥1–2 GB |
Escrituras sostenidas | ≥100 000 pts/s | 10 000–50 000 pts/s |
Latencia de escritura | <5 ms | 10–20 ms |
Agregaciones en servidor | Flux (CQ, Tasks) | Continuous Aggregates |
Gestión de retención automática | RP nativas | drop_chunks manual |
Compresión de datos antiguos | 2–5× (TSM) | 10–20× (chunks) |
Dependencias operativas | Solo InfluxDB | Postgres + TS ext. |
Opción 1: TimescaleDB
-
Ventajas
- Todo el poder de SQL y extensiones de Postgres.
- Continuous aggregates materializados para downsampling automático.
- Compresión de chunks muy agresiva.
-
Desventajas
- Arranque de Postgres +
shared_buffers
(≥1 GB RAM). - Configuración de vacuums, WAL, checkpoints y particiones.
- Escrituras TS en Postgres con overhead de MVCC.
- Arranque de Postgres +
Opción 2: InfluxDB
-
Ventajas
- Solo un binario Go (∼200 MB).
- Escrituras masivas nativas (≥100 000 pts/s).
- Retention Policy (RP) y Continuous Queries (CQ) integradas.
- Consultas Flux potentes:
from(bucket:"raw_analytics") |> range(start: -30d) |> filter(fn: (r) => r._measurement == "discovery") |> aggregateWindow(every: 1h, fn: sum) |> derivative(unit: 1m)
- Footprint RAM y CPU mucho más bajo que Postgres.
-
Desventajas
- Lenguaje Flux requiere aprendizaje.
- No soporte nativo de joins complejos.
- Clustering nativo solo en versión Enterprise.
Comparando InfluxDB vs TimescaleDB
Característica | InfluxDB | TimescaleDB |
---|---|---|
Modelo TS | Tag/Field | Hipertabla Postgres + chunks |
Ingesta máx. | ≥100 000 pts/s | 10 000–50 000 pts/s |
Latencia | <5 ms | 10–20 ms |
Memoria | 200–500 MB | 1–2 GB |
Compresión | TSM (2–5×) | Chunks (10–20×) |
Retención | Policy integrada | drop_chunks + políticas Timescale |
Consultas | Flux pipelines | SQL + time_bucket |
Operación | Muy simple (1 binario) | Compleja (tuning de Postgres) |
Conclusión
Después de pruebas reales con nuestro flujo Redis → InfluxDB → Postgres, el balance fue claro:
- InfluxDB manejó picos de 100 000 pts/s sostenidos sin apenas CPU extra.
- Con solo 300 MB de RAM logró persistir e indexar correctamente.
- Sus Retention Policies descartaron automáticamente datos a 30 días, sin necesidad de scripts.
- Las Continuous Queries y Flux nos permitieron calcular downsampling diario in situ, reduciendo carga en Postgres.
- Operativamente, desplegar un único servicio Go es mucho más ligero que afinar un clúster de Postgres.
En mi caso, el volumen moderado de métricas y la baja complejidad de agregaciones (sumas diarias, conteos, HyperLogLog) no justificaban la sobrecarga de memoria y mantenimiento de TimescaleDB. La filosofía de “una sola responsabilidad” de InfluxDB —un TSDB puro listo para producción— cubrió perfectamente mis necesidades con un footprint mínimo.
Por eso, tras evaluar ambas opciones en detalle, elegí InfluxDB como motor de series temporales en mi stack. Su rendimiento de ingesta, facilidad de configuración y bajo consumo de recursos justifican plenamente mantenerlo junto a Redis y Postgres como piezas complementarias en nuestra arquitectura de métricas.
Todavía hay un punto crítico por definir: aunque he hablado sobre la inserción de datos, falta definir la lectura o recuperación, pues ahora tengo datos en Redis (Buffer), InfluxDB (Hot-Path) y Postgres (Cold-Path). ¿Cuál debería mostrar? Esto lo dejaré para un siguiente artículo.
Happy coding! :D
Photo by Jan Sents on Unsplash
Written with StackEdit.