04 - Time-Series (InfluxDB, Prometheus)
video: https://youtu.be/... no frontmatter da nota.Time-Series (InfluxDB, Prometheus)
Bancos de dados time-series (TSDB) são otimizados para o padrão de dados mais comum da operação de sistemas: um identificador contínuo (a série), um timestamp crescente e um ou mais valores amostrados no tempo. Métricas de infraestrutura, telemetria de IoT, preços de mercado e eventos de sensores compartilham esse formato, e o volume é brutal; milhares de séries amostradas a cada segundos geram milhões de pontos por hora. Em produção, o TSDB resolve o que nenhum banco generalista faz bem ao mesmo tempo: ingestão de escrita contínua em alta taxa, compressão agressiva de pontos que diferem pouco entre si, retenção automática por idade e agregações eficientes por janela de tempo. InfluxDB é o exemplo de TSDB de propósito geral, com linguagem de consulta própria e camadas de retenção; Prometheus é o padrão de facto para métricas de infraestrutura e aplicações, com modelo pull, PromQL e integração nativa com o ecossistema de observabilidade. Este arquivo cobre a natureza dos dados temporais, os mecanismos internos de compressão e retenção, a cardinalidade como risco central e a comparação prática entre os dois motores.
Conceito
Uma série temporal é identificada pelo conjunto de rótulos (tags) que descrevem sua origem, e evolui como uma sequência de pares timestamp e valor. No modelo do Prometheus, a série é definida pelo nome da métrica mais o conjunto de labels; http_requests_total{service="checkout", method="POST"} é uma série distinta de qualquer variação de label. No InfluxDB, a combinação de measurement, tag set e field cumpre papel equivalente. Três propriedades distinguem esse modelo do relacional: os dados são quase imutáveis (append only), as consultas são por janela de tempo e agregação, e o valor analítico decai com a idade, o que justifica retenção e resolução decrescentes. A granularidade da amostragem é uma decisão de desenho: amostrar a cada 10 segundos multiplicar por 6 o custo de amostrar a cada minuto, sem ganho analítico quando a métrica só interessa em janelas de minutos. Tudo no TSDB gira em torno de equilibrar resolução, janela de interesse e custo.
Que problema resolve
O TSDB existe porque dados temporais quebram as premissas de custo e desempenho dos bancos generalistas. Guardar pontos em um relacional comum gera tabelas gigantes, índices inflados e consultas de agregação cada vez mais lentas conforme o histórico cresce. O TSDB resolve quatro problemas específicos:
- Ingestão contínua de alta taxa: escritas append-only em lotes, sem contenção de transação, sustentam milhões de pontos por segundo por nó.
- Compressão por semelhança: timestamps delta-of-delta, valores com codificação gorilla e dicionário de tags comprimem dados repetitivos em frações do tamanho bruto.
- Retenção automática: políticas de retenção descartam ou agregam dados velhos sem job manual, mantendo o disco em regime estacionário.
- Consultas por janela: funções de agregação por intervalo (
range_vector,GROUP BY time()) são operações de primeira classe, não hacks sobre SQL.
O resultado prático é que consultas como a média de CPU por hora nas últimas 4 semanas respondem em segundos, o que seria inviável varrendo bilhões de pontos brutos em um banco convencional.
Funcionamento interno
Os motores time-series compartilham uma arquitetura interna semelhante, inspirada em LSM trees e colunas comprimidas. A escrita entra em um log appendido (WAL) para durabilidade e em um cache de memória; quando o cache enche, o motor descarrega blocos imutáveis em disco, cada um cobrindo um intervalo de tempo. O InfluxDB organiza dados em shards por duração de retenção, cada um com seu TSM (Time-Structured Merge) tree, e o motor de índice mapeia tags para as séries correspondentes. O Prometheus mantém um head block em memória e materializa blocos de 2 horas em disco, compactando-os em blocos maiores com o tempo. A leitura de uma consulta identifica primeiro as séries pelo índice de tags, depois varre sequencialmente os blocos do intervalo pedido, decomprimindo apenas o necessário; esse acesso sequencial é a razão do desempenho estável com o histórico grande. A compactação em segundo plano mescla blocos, reescreve com compressão melhor e remove pontos expirados, funcionando como o vacuum do mundo métrico.
-- InfluxDB (Flux): média por janela de 5 minutos e downsampling
from(bucket: "telemetria/autogen")
|> range(start: -6h)
|> filter(fn: (r) => r._measurement == "sensor_temp" and
r.regiao == "sudeste")
|> aggregateWindow(every: 5m, fn: mean, createEmpty: false)
|> to(bucket: "telemetria/downsampled_5m") // persiste o rollup
# PromQL: p95 de latência por serviço nas últimas 5 minutos
histogram_quantile(0.95,
sum by (service, le) (
rate(http_request_duration_seconds_bucket[5m])
)
)
# Taxa de erros normalizada por requisições, em percentual
100 * sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
Retenção, downsampling e rollups
A política de dados temporais em produção tem três camadas com resoluções distintas. A camada bruta mantém a resolução original por uma janela curta, tipicamente 7 a 30 dias, para investigação fina de incidentes. A camada de rollup agrega por janelas maiores (média, máximo, mínimo e contagem a cada 5 minutos ou hora) e vive por meses, alimentando dashboards gerenciais e comparações históricas. A camada analítica, com resolução diária ou mensal, sustenta tendências de longo prazo a custo mínimo. No InfluxDB, essa estratégia se materializa em retention policies e tasks de downsampling que gravam os agregados em buckets de duração maior. No Prometheus, o padrão complementar é federar ou exportar dados antigos para um armazenamento de longo prazo (Thanos, Cortex, Mimir ou a própria integração com TSDBs externas), pois o Prometheus local não é a ferramenta ideal para histórico de meses. A regra de desenho é nunca fazer a pergunta gerencial na camada bruta; se o dashboard de capacidade consulta pontos de 10 segundos de 90 dias atrás, o desenho está errado.
Cardinalidade de séries
Cardinalidade é o número total de séries distintas, e é o principal risco operacional de um TSDB. Cada label adicionada multiplica a cardinalidade: uma métrica com user_id como label pode gerar uma série por usuário, milhões delas, e o índice de séries consome memória até degradar o motor inteiro. O sintoma clássico é o out-of-memory no Prometheus horas após alguém incluir um label de alta cardinalidade, e a recuperação exige reinício ou reescrita de blocos. As diretrizes práticas são claras: labels devem conter dimensões de agregação limitadas e conhecidas (região, serviço, endpoint), nunca identificadores ilimitados (user_id, session_id, request_id); filtros de alta cardinalidade pertencem a logs e traces, não a métricas. Monitore prometheus_tsdb_head_series e a cardinalidade por métrica, defina limites de séries por exportador e revise o desenho de labels antes de cada nova métrica, porque séries criadas permanecem no índice por um ciclo completo de retenção.
InfluxDB versus Prometheus
| Critério | InfluxDB | Prometheus |
|---|---|---|
| Modelo de coleta | Push (clientes e integrações enviam) | Pull (scrape de endpoints /metrics) |
| Linguagem de consulta | InfluxQL e Flux | PromQL |
| Cardinalidade típica | Alta (tags numerosas por design) | Controlada (modelo de labels restrito) |
| Retenção | Nativa por bucket e shard | Curta no local; longo prazo via Thanos/Mimir |
| Histórico longo | Nativo com downsampling | Requer camada externa |
| Alertas | Via integrações e cloud | Alertmanager nativo |
| Caso forte | IoT, eventos industriais, séries de negócio | Métricas de infraestrutura e aplicações cloud-native |
Os dois motores se complementam mais do que competem. Prometheus domina a observabilidade de sistemas cloud-native: instrumentação padrão, service discovery, alertas e ecossistema consolidado, com a ressalva de que histórico longo exige arquitetura adicional. InfluxDB brilha em telemetria de dispositivos e IoT, ingestão push com metadados ricos por dispositivo e retenção nativa em camadas. Em uma mesma empresa é comum operar os dois: Prometheus para saúde da plataforma e InfluxDB (ou alternativa similar) para dados de produtos conectados, com roteamento por natureza do dado.
Arquitetura de coleta do Prometheus
O modelo pull do Prometheus define o desenho da coleta e as implicações de operação. O servidor descobre alvos por service discovery (Kubernetes, Consul, DNS), faz scrape dos endpoints /metrics em intervalos fixos e armazena os pontos localmente; a descoberta automática acompanha réplicas subindo e caindo sem configuração manual. Para alta disponibilidade, rodam pares idênticos de Prometheus independentes, cada um coletando tudo, e o Alertmanager deduplica alertas entre eles. A federação permite que um Prometheus de nível superior colete séries agregadas de instâncias de nível inferior, útil para visões globais leves. Para histórico longo, os blocos TSDB são replicados para armazenamento de objeto via Thanos ou Mimir, com consulta global unificada sobre os blocos. O pull simplifica: se um alvo não responde, o problema é visível como up == 0 na própria métrica, sem buffers do lado do cliente estourando silenciosamente.
# prometheus.yml: scrape com service discovery e regras de alerta
scrape_configs:
- job_name: "servicos-app"
scrape_interval: 15s
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"
- job_name: "infra"
static_configs:
- targets: ["node-exporter-1:9100", "node-exporter-2:9100"]
rule_files:
- "/etc/prometheus/regras/*.yml" # regras de alerta versionadas
Casos de uso e padrões de aplicação
A escolha de TSDB se justifica em três famílias de caso. Em telemetria de infraestrutura, métricas de CPU, memória, latência e filas alimentam SLOs e alertas, com Prometheus como padrão consolidado. Em IoT e sensores, dispositivos enviam leituras periódicas de temperatura, pressão, consumo e posição; a ingestão push e a retenção em camadas do InfluxDB encaixam bem no volume e no ciclo de vida do dado. Em métricas de negócio em tempo real, contadores de pedidos, conversão por canal e assinaturas ativas recebem o mesmo tratamento de séries, permitindo dashboards executivos com a mesma infraestrutura. O padrão comum aos três casos é a pergunta por janela: quanto, quando e com que tendência; perguntas que exigem o detalhe por ocorrência pertencem a logs e traces, e misturar as duas necessidades no mesmo sistema é erro de arquitetura.
| Caso de uso | Coleta | Resolução típica | Retenção bruta | Retenção agregada |
|---|---|---|---|---|
| Métricas de plataforma | Pull 15s | 15 segundos | 15 a 30 dias | 1 ano (5 min) |
| IoT industrial | Push por dispositivo | 1 a 60 segundos | 30 dias | 5 anos (1 h) |
| Métricas de negócio | Push via gateway | 1 minuto | 90 dias | 2 anos (1 h) |
| Sinais de alta frequência | Push contínuo | Milissegundos | 7 dias | 1 ano (1 min) |
Erros comuns
- Labels de alta cardinalidade (IDs de usuário, hashes de requisição) em métricas, explodindo memória do TSDB e forçando reinício em pleno horário comercial.
- Não definir política de retenção no primeiro dia, acumulando resolução bruta por anos e transformando o disco em gargalo crescente.
- Dashboards gerenciais consultando dados brutos em vez de rollups, com consultas de minutos que pesam sobre todo o cluster.
- Amostragem mais fina do que o necessário, multiplicando custo de armazenamento e ingestão sem ganho analítico correspondente.
- Confundir métricas com eventos: fatos discretos que exigem detalhe por ocorrência pertencem a logs ou event stores, não a séries agregadas.
- Ignorar o relógio dos produtores, aceitando pontos com timestamps no futuro ou fora de ordem que distorcem janelas e bloqueiam compactação.
- Falta de alerta sobre ingestão e cardinalidade, descobrindo a degradação apenas quando o motor para de responder.
- Exportadores expostos sem limites de job, duplicando séries por labels de instância variável (IP efêmero) em vez de rótulo estável de serviço.
- Timestamps no fuso errado ou em segundos onde o motor espera milissegundos, deslocando janelas e corrompendo agregações.
Boas práticas
- Desenhe a taxonomia de métricas com labels de cardinalidade limitada e documente, por métrica, quais labels são permitidos.
- Estabeleça a pirâmide de retenção desde o início: bruta curta, rollup de 5 minutos por meses, agregado diário por anos.
- Versione dashboards e regras de alerta como código (prometheus.yml, tasks de Flux), revisados como qualquer mudança de produção.
- Padronize instrumentação com OpenTelemetry ou bibliotecas oficiais, evitando métricas ad hoc que fragmentam a taxonomia.
- Monitore a saúde do próprio TSDB: séries no head block, taxa de ingestão, duração de compactação e uso de memória.
- Teste recuperação de desastre com restauração de snapshots e verificação de blocos, pois TSDBs são infraestrutura crítica de operação.
- Separe ambientes e tenants por instância ou bucket quando a cardinalidade for alta, contendo o raio de explosão de uma carga anômala.
- Padronize nomes de métrica com convenção de unidade no sufixo (seconds, bytes, total) para que alertas e dashboards reutilizem as mesmas séries.
- Revise trimestralmente a lista de métricas vivas, aposentando séries sem consumidor e devolvendo capacidade de ingestão ao sistema.