Séries temporais
Bancos otimizados para medições ao longo do tempo, com retenção e agregação.
As decisões com séries temporais são cinco: qual banco, como particionar, quanto guardar de cada resolução, como resumir e como lidar com a cardinalidade.
Qual banco
| Opção | Bom para | Custos |
|---|---|---|
| PostgreSQL comum, com particionamento por data | Volumes moderados, quando não se quer extensão | Retenção, compressão e resumos feitos à mão |
| TimescaleDB | Medições de negócio ao lado dos dados relacionais, com SQL completo | Uma extensão a instalar; nem todo PostgreSQL gerenciado a oferece |
| Prometheus | Métricas de servidores e aplicações, com alertas | Não é para dados de negócio nem para guardar por anos; consulta em PromQL |
| InfluxDB, QuestDB | Ingestão muito alta de medições, em banco dedicado | Outro banco, com linguagem e ecossistema próprios |
| ClickHouse | Séries temporais com análises pesadas e muitas dimensões | Ver o tópico de bancos colunares |
| Serviços gerenciados (Timestream, Azure Data Explorer, Grafana Cloud) | Sem operar servidores | Custo por volume |
A pergunta que separa os casos: o dado é métrica de sistema (CPU, latência, erros) ou dado de negócio (leituras de sensores de clientes, preços, consumo)? Para métricas, o ecossistema do Prometheus é o padrão. Para dados de negócio, que se cruzam com clientes e contratos, o TimescaleDB mantém tudo no mesmo banco.
Como particionar
| Decisão | Recomendação |
|---|---|
| Tamanho do pedaço | Que os pedaços recentes, com os índices, caibam na memória; um dia é um começo comum |
| Índice | Pelas colunas que filtram junto com o tempo: (sensor_id, ts DESC) |
| Agrupar ao comprimir | Pela coluna mais filtrada (segmentby = 'sensor_id'), ordenando pelo tempo |
| Quando comprimir | Depois que o dado para de mudar: dias ou semanas, conforme o atraso com que as medições chegam |
Quanto guardar de cada resolução
A política mais comum guarda cada resolução por um tempo diferente:
| Resolução | Por quanto tempo | Para quê |
|---|---|---|
| Cada medição | 7 a 30 dias | Investigar o detalhe recente |
| Média por hora | 1 a 2 anos | Painéis e comparações |
| Média por dia | Para sempre, ou anos | Tendências e relatórios |
Com o TimescaleDB, isso são três políticas: retenção nas medições brutas (add_retention_policy), um resumo contínuo por hora com retenção própria e outro por dia. No teste do nível Conceito, apagar 3 dias com DELETE levou 4,4 segundos e não devolveu espaço; o drop_chunks levou 115 ms e devolveu 523 MB.
Antes de apagar o bruto, confira que os resumos cobrem o período: uma vez apagado, o minuto a minuto não volta.
Como resumir
| Forma | Atualização | Custo |
|---|---|---|
| Calcular na consulta | Sempre atual | Recalcula a cada acesso: 127 ms no teste |
| Visão materializada comum | Só quando atualizada, e recalcula tudo | Simples; pesada com muito dado |
| Resumo contínuo (TimescaleDB) | Incremental, por política | 7,7 ms no teste; mostra só o materializado, por padrão |
| Resumo contínuo com dados recentes em tempo real | materialized_only = false: junta o resumo com o bruto ainda não materializado | Um pouco mais lento, sempre atual |
Cuidado ao combinar resumos: a média das médias por hora só é a média do dia se todas as horas tiverem o mesmo número de medições. Guarde a soma e a contagem (ou a média e a contagem), e combine com sum(media * n) / sum(n), como a consulta do teste fez.
Cardinalidade
Em bancos de métricas, cada combinação de nome e rótulos é uma série separada: requisicoes{rota="/pedidos", status="200"}. O número de séries é a cardinalidade, e é ela que pesa na memória e no disco.
| Rótulo | Valores possíveis | Efeito |
|---|---|---|
status | Uma dúzia | Seguro |
rota (o modelo, como /pedidos/{id}) | Dezenas | Seguro |
rota (a URL real, como /pedidos/981) | Ilimitados | Uma série por pedido: explode |
usuario_id, pedido_id, request_id | Ilimitados | Nunca como rótulo de métrica; vai para logs ou rastreamento |
Resumo da decisão
- Prometheus para métricas de sistema; TimescaleDB (ou outro banco de séries) para medições de negócio.
- Particionar por tempo, com índice pela coluna que filtra junto com o tempo.
- Retenção por resolução: bruto por dias, resumos por anos, com
drop_chunkse nuncaDELETEem massa. - Comprimir o que parou de mudar.
- Resumos contínuos com soma e contagem, e saber se mostram os dados mais recentes.
- Gravar em lotes, e rótulos de métricas só com poucos valores.
O próximo nível mostra a gravação em lote e a média por hora nas cinco linguagens.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Escolher onde guardar minhas séries temporais
Preciso guardar séries temporais no meu sistema. Pergunte o que é medido, quantas medições por segundo, por quanto tempo guardar, que consultas e painéis existem e se os dados se cruzam com outras tabelas do sistema. Compare para o meu caso PostgreSQL com particionamento, TimescaleDB, Prometheus, InfluxDB e ClickHouse. Recomende uma opção e explique a política de retenção, compressão e resumos.
Desenhar a retenção dos dados
Quero definir quanto tempo guardar cada resolução das minhas séries temporais. Pergunte o que os usuários consultam, com que detalhe e de que período, e se há exigências legais ou contratuais. Monte comigo a política: o que guardar bruto e por quanto tempo, quais resumos (por hora, por dia) e por quanto tempo, quando comprimir, e como confirmar que os resumos cobrem o período antes de apagar o bruto.
Métricas da aplicação sem explodir a cardinalidade
Quero instrumentar minha aplicação com métricas sem criar séries demais. Pergunte qual linguagem e qual sistema de métricas eu uso, e o que eu quero acompanhar. Me ajude a definir as métricas, os rótulos de cada uma com os valores possíveis, o que vai para logs e rastreamento em vez de métricas, e como estimar o número de séries antes de colocar em produção.