ClickHouse: OLAP analytics de alto throughput
- ⬜⚡ DynamoDB: single-table design(NoSQL + Vector Databases)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Por que ClickHouse existe
Postgres é row-oriented: uma row vive junta em disco. Excelente para OLTP (ler/atualizar 1 row inteira). Péssimo para analytics (SELECT SUM(x) em 1B rows lê todas as colunas, mesmo usando só x). ClickHouse inverte: colunar. Cada coluna é um arquivo comprimido. Query agregada lê só as colunas necessárias.
Schema real: eventos de analytics
CREATE TABLE events
(
event_time DateTime64(3) CODEC(DoubleDelta, LZ4),
event_name LowCardinality(String),
user_id UInt64,
session_id UUID,
country LowCardinality(FixedString(2)),
device LowCardinality(String),
url String CODEC(ZSTD(3)),
referrer String CODEC(ZSTD(3)),
properties Map(String, String)
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(event_time)
ORDER BY (event_name, event_time, user_id)
TTL event_time + INTERVAL 18 MONTH
SETTINGS index_granularity = 8192;LowCardinality é dicionário automático para colunas com < 10k valores únicos (event_name, country). Reduz tamanho em 10x. CODEC(DoubleDelta, LZ4) em timestamp aproveita que eventos são quase sequenciais — compressão 50x+. Essas decisões de schema têm impacto brutal em custo.
Query analítica real
-- Top 10 paginas por unique users nos ultimos 7 dias,
-- com conversion rate, segmentado por pais
SELECT
url,
country,
uniqExact(user_id) AS unique_users,
countIf(event_name = 'page_view') AS views,
countIf(event_name = 'purchase') AS purchases,
round(purchases / unique_users * 100, 2) AS conversion_pct
FROM events
WHERE event_time >= now() - INTERVAL 7 DAY
AND country IN ('BR', 'US', 'PT')
GROUP BY url, country
HAVING unique_users > 100
ORDER BY conversion_pct DESC
LIMIT 10
SETTINGS max_threads = 8;Em tabela de 2 bilhões de rows, essa query roda em ~3-5s num nó de 8 cores. Postgres mesmo com índices levaria minutos.
Por que bancos colunares dominam cargas analíticas?
Materialized views para rollups
-- Rollup diario por pais (atualiza em tempo real)
CREATE MATERIALIZED VIEW events_daily
ENGINE = SummingMergeTree
PARTITION BY toYYYYMM(day)
ORDER BY (day, country, event_name)
AS SELECT
toDate(event_time) AS day,
country,
event_name,
count() AS events,
uniqState(user_id) AS users_state
FROM events
GROUP BY day, country, event_name;
-- Query sobre o rollup (instantanea)
SELECT day, country, sum(events) AS total
FROM events_daily
WHERE day >= today() - 30
GROUP BY day, country
ORDER BY day DESC;- → blocos
- → a cada inserção
- Compute
- Conceito de arquitetura
- Analytics
Repare que a seta da agregação sai da tabela crua no momento da inserção, e não do painel no momento da consulta. É essa inversão — pagar na escrita para que a leitura seja barata — que sustenta painel interativo sobre volume que não caberia numa consulta ao vivo.
- 1 · Inserir linha a linha destrói o banco. Cada inserção cria um pedaço em disco que depois precisa ser mesclado. Milhares de inserções pequenas por segundo geram milhares de pedaços e a mesclagem não acompanha — o sintoma é o banco recusar escrita.
- 2 · O armazenamento colunar é o que baratea a leitura. Uma consulta que soma uma coluna lê apenas aquela coluna. Num formato por linha, ela leria todas as colunas de todas as linhas — a diferença é de ordens de grandeza em tabela larga.
- 3 · A chave de ordenação decide o que é rápido. Os dados são fisicamente ordenados por ela, então filtrar pelo seu prefixo lê poucos blocos. Filtrar por coluna fora dessa ordem lê tudo — mesma consulta, mil vezes o custo.
- 4 · A agregação acontece na entrada, não na consulta. A visão materializada é acionada por inserção e escreve o estado parcial já agregado. O painel não recalcula bilhões de linhas: ele lê milhares.
- 5 · O cru continua ali para a pergunta nova. É a vantagem sobre pré-agregar e descartar: quando alguém quiser um corte que ninguém previu, os eventos originais ainda existem — e a retenção se controla descartando partições inteiras.
Materialized view em ClickHouse é trigger: cada insert na tabela base alimenta a MV. Diferente de Postgres, onde MV precisa REFRESH.
Ingestion: sempre em batch
Nunca faça single-row INSERT em ClickHouse em produção. Mínimo ~10k rows por batch, idealmente 100k+. Use Kafka engine, Buffer tables, ou aplicação com buffering em memória. Single inserts geram too-many-parts e matam a performance.
ClickHouse é a resposta quando Postgres gasta horas numa query analítica. Mas não é substituto — é complemento via CDC. OLTP fica no Postgres, analytics vai para ClickHouse.
Perguntas frequentes
❓ Quando um banco colunar de alta vazão vence o armazém?
❓ Por que não usar como banco de aplicação?
❓ Como lidar com duplicata na ingestão?
Fixando
Para que servem visões materializadas com agregação prévia nesse contexto?
Qual é a limitação que precisa ser aceita ao escolher esse tipo de banco?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…