De RDS para ClickHouse: 100M de mensagens/dia com p99 abaixo de 10ms
Benchmark de fundo sobre como eliminar contenção de locks em bancos relacionais e migrar para armazenamento colunar em consultas de telemetria de sub-segundo.
- Publicado em
leitura de 12 min
O sintoma era sempre o mesmo: o dashboard de telemetria levava 30 segundos para responder, e o time de infraestrutura respondia que o banco estava são. Estava. Ele só nunca foi desenhado para a pergunta que estávamos fazendo.
A causa raiz é o modelo de acesso, não o volume
Telemetria é um caso de leitura analítica puro: agregações de baixo cardinalidade sobre janelas de tempo, quase nunca de volta a uma linha individual. Um banco relacional transacional resolve esse padrão da forma mais cara possível: B-tree, page split, contenção de buffer pool e um plano de execução que precisa tocar milhões de linhas para responder o que é uma soma.
Bancos colunares invertem todas essas decisões. Colunas são independentes, então uma agregação lê apenas as colunas necessárias. Partições por tempo tornam a poda quase gratuita. E a compressão por coluna reduz a varredura em uma ordem de grandeza.
O caminho, na ordem em que funciona
- Não migre o banco. Migre a responsabilidade: extraia o fluxo de telemetria para um caminho de escrita dedicado e deixe o relacional cuidar do que ele faz bem.
- ETL com Databricks para normalizar, deduplicar e agregar antes da carga. O mais rápido insert em ClickHouse é o que nunca aconteceu.
- Escolha a chave de ordenação pela consulta mais frequente, não pela entidade. Em telemetria, quase sempre é (timestamp, device_id).
- Materialize visões para as consultas que rodam a cada cinco minutos e mantenha o resto em consultas ad-hoc.
CREATE TABLE telemetry_events (
occurred_at DateTime64(3, 'UTC') CODEC(Delta(8), ZSTD(1)),
device_id UInt64 CODEC(ZSTD(1)),
event_type LowCardinality(String) CODEC(ZSTD(1)),
payload String CODEC(ZSTD(3)),
ingested_at DateTime64(3, 'UTC') DEFAULT now()
) ENGINE = MergeTree
PARTITION BY toYYYYMM(occurred_at)
ORDER BY (event_type, occurred_at, device_id)
TTL toDateTime(occurred_at) + INTERVAL 25 MONTH;Resultados medidos
- Tempo da consulta do dashboard: 30s → 190ms.
- Carga no banco relacional: −72%, porque ele deixou de ser o caminho analítico.
- Throughput de ingestão: 100M de mensagens/dia com p99 de ingestão abaixo de 10ms.
- Armazenamento: −88% frente ao relacional equivalente, por compressão colunar e TTL nativo.
Quando não migrar
Se a consulta é por chave primária, se o volume cabe folgadamente na memória e se ninguém precisa agregar milhões de linhas, o relacional continua sendo a escolha certa. Arquitetura séria inclui saber quando não usar a ferramenta que você acabou de dominar.