ClickHouse: banco de dados analítico para quem precisa de velocidade real
1. Arquitetura Colunar: O Segredo da Performance Analítica
1.1 Armazenamento orientado a colunas vs. linhas: impacto direto em consultas OLAP
ClickHouse é um banco de dados colunar projetado para processamento analítico online (OLAP). Enquanto bancos relacionais tradicionais armazenam dados linha por linha, o ClickHouse armazena cada coluna em blocos separados. Isso transforma consultas que varrem milhões de linhas: em vez de ler dados desnecessários de colunas não utilizadas, o banco lê apenas as colunas relevantes.
Exemplo prático: Em uma tabela com 100 colunas, uma consulta que soma vendas por mês lê apenas 2 colunas (valor e data), reduzindo o I/O em 98%.
1.2 Compressão agressiva por coluna e redução de I/O em scans massivos
Dados do mesmo tipo armazenados juntos permitem compressão extremamente eficiente. ClickHouse usa codecs como LZ4, ZSTD, Delta e DoubleDelta. Uma coluna de timestamps pode ser comprimida em 10-20% do tamanho original.
CREATE TABLE vendas (
data DateTime CODEC(DoubleDelta, LZ4),
produto_id UInt32 CODEC(ZSTD(3)),
valor Float64 CODEC(Gorilla, LZ4)
) ENGINE = MergeTree()
ORDER BY data;
1.3 Particionamento físico e ordenação primária (ORDER BY) como índices implícitos
A cláusula ORDER BY na criação da tabela define a ordenação física dos dados no disco. Combinada com particionamento (PARTITION BY), cria um índice esparso que permite pular blocos inteiros que não contêm dados relevantes.
CREATE TABLE logs_acesso (
timestamp DateTime,
usuario_id UInt32,
pagina String,
duracao_segundos UInt16
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (usuario_id, timestamp);
2. Modelagem de Dados para Máximo Rendimento
2.1 Esquemas otimizados: tabelas MergeTree, materialized views e AggregatingMergeTree
O motor MergeTree é a base. Para agregações pré-calculadas, use AggregatingMergeTree com funções de estado:
CREATE MATERIALIZED VIEW vendas_diarias
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(dia)
ORDER BY (produto_id, dia)
AS SELECT
toDate(data) AS dia,
produto_id,
sumState(valor) AS receita_total,
countState() AS total_vendas
FROM vendas
GROUP BY dia, produto_id;
2.2 Escolha de chave de particionamento e ordenação para consultas típicas
- Ordenação: coloque colunas de filtro frequentes primeiro (ex:
(cliente_id, data)) - Particionamento: use
toYYYYMM(data)para séries temporais; evite partições com menos de 100MB - Amostragem:
SAMPLE BYpermite consultas aproximadas rápidas
2.3 Uso de tipos de dados específicos
DateTime64(3)para timestamps com milissegundosLowCardinality(String)para colunas com até ~10.000 valores únicos (reduz memória e acelera joins)SimpleAggregateFunction(sum, Float64)para agregados leves em tabelas de agregação
CREATE TABLE eventos (
timestamp DateTime64(3),
tipo_evento LowCardinality(String),
usuario_id UInt32,
metrica Float32
) ENGINE = MergeTree()
ORDER BY (tipo_evento, timestamp);
3. Ingestão em Escala: Streaming e Carga em Lote
3.1 Inserção assíncrona e batch insert: evitando micro-partições ineficientes
Inserções individuais criam partes pequenas que degradam performance. Use lotes de 10.000-100.000 linhas:
INSERT INTO vendas VALUES
(now(), 1, 100.50),
(now(), 2, 250.00),
... (10.000 linhas)
Ative async_insert=1 no cliente para buffer automático.
3.2 Integração com Kafka (Kafka Engine) e filas de mensagens em tempo real
CREATE TABLE kafka_queue (
mensagem String
) ENGINE = Kafka
SETTINGS
kafka_broker_list = 'localhost:9092',
kafka_topic_list = 'eventos',
kafka_group_name = 'clickhouse_consumer',
kafka_format = 'JSONEachRow';
CREATE MATERIALIZED VIEW kafka_to_vendas
TO vendas AS
SELECT
JSONExtractString(mensagem, 'produto') AS produto,
JSONExtractFloat(mensagem, 'valor') AS valor,
now() AS data
FROM kafka_queue;
3.3 Tabelas buffer e estratégias de flush para alta taxa de escrita
Tabelas Buffer acumulam inserções e fazem flush periódico para a tabela principal:
CREATE TABLE vendas_buffer AS vendas
ENGINE = Buffer(vendas, 16, 10, 100, 10000, 1000000, 10000000, 100000000, 1000000000);
4. Execução de Consultas e Otimização de Queries
4.1 Pipeline de execução vetorizada e processamento SIMD
ClickHouse processa dados em vetores de 1024 elementos por vez, utilizando instruções SIMD da CPU. Isso permite processar bilhões de linhas por segundo em hardware commodity.
4.2 Uso de funções analíticas sem subqueries
SELECT
produto_id,
sum(valor) AS receita,
quantile(0.95)(valor) AS p95_valor,
retention(
data >= '2024-01-01',
data >= '2024-02-01'
) AS retencao_mensal
FROM vendas
GROUP BY produto_id;
4.3 Identificação de gargalos com EXPLAIN e system.query_log
EXPLAIN SYNTAX
SELECT count() FROM vendas WHERE data > '2024-01-01';
-- Ver queries lentas:
SELECT
query,
query_duration_ms,
read_rows,
read_bytes,
memory_usage
FROM system.query_log
WHERE type = 'QueryFinish'
AND query_duration_ms > 1000
ORDER BY query_duration_ms DESC;
5. Estratégias de Replicação e Alta Disponibilidade
5.1 ReplicatedMergeTree: replicação síncrona sem dependência de coordenação externa
CREATE TABLE vendas_replicado ON CLUSTER cluster_3_nodes (
data DateTime,
produto_id UInt32,
valor Float64
) ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/vendas',
'{replica}'
)
ORDER BY data;
5.2 Failover automático e consistência eventual em clusters multi-nó
O ClickHouse usa ZooKeeper ou ClickHouse Keeper para coordenação. Se um nó falha, as réplicas continuam servindo consultas. A consistência é eventual, mas suficiente para workloads analíticos.
5.3 Backup e restore com tabelas Freeze
-- Criar snapshot
ALTER TABLE vendas FREEZE;
-- Backup físico
cp -r /var/lib/clickhouse/shadow/ /backup/clickhouse_$(date +%Y%m%d)
6. ClickHouse na Prática: Casos de Uso e Armadilhas Comuns
6.1 Telemetria e observabilidade: logs, métricas e traces com alto throughput
Empresas como Cloudflare e Uber processam petabytes de logs diariamente com ClickHouse. A chave é usar ORDER BY (timestamp, host) e particionamento por hora.
6.2 Análise de séries temporais e dashboards em tempo real (Grafana + ClickHouse)
Grafana tem suporte nativo ao ClickHouse. Configure datasources e use funções como toStartOfInterval() para agregações temporais:
SELECT
toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS minute,
avg(latencia_ms) AS latencia_media,
quantile(0.99)(latencia_ms) AS p99
FROM metricas
WHERE timestamp > now() - INTERVAL 1 HOUR
GROUP BY minute
ORDER BY minute;
6.3 Erros frequentes: joins desnecessários, cardinalidade mal escolhida e falta de TTL
- Joins: Use
dictGet()para lookup tables em vez de JOINs - Cardinalidade: Evite
ORDER BYcom colunas de alta cardinalidade (ex: UUID) - TTL: Configure expiração automática para dados antigos:
ALTER TABLE logs_acesso MODIFY TTL timestamp + INTERVAL 90 DAY;
7. Comparação com Alternativas e Limitações Reais
7.1 ClickHouse vs. Druid, Pinot e TimescaleDB
| Característica | ClickHouse | Druid | Pinot | TimescaleDB |
|---|---|---|---|---|
| Inserção em tempo real | Excelente (Kafka Engine) | Excelente | Excelente | Bom |
| Consultas complexas | Excelente | Médio | Médio | Bom |
| Compressão | Excelente (5-15x) | Bom (3-8x) | Bom (3-8x) | Médio (2-4x) |
| Facilidade de operação | Médio (ZooKeeper) | Complexo | Complexo | Fácil |
7.2 Limitações de UPDATE/DELETE e transações
ClickHouse não suporta transações ACID tradicionais. Para atualizações, use ALTER TABLE ... UPDATE (mutação pesada) ou recrie partições. Prefira modelos de dados append-only.
7.3 Quando NÃO usar ClickHouse
- Workloads OLTP com muitas atualizações e transações curtas
- Joins complexos entre muitas tabelas grandes
- Dados altamente mutáveis (ex: carrinhos de compras)
- Consultas que exigem consistência imediata após escrita
Referências
- Documentação Oficial do ClickHouse — Guia completo de sintaxe, motores de tabela e configuração
- ClickHouse: The Definitive Guide (O'Reilly) — Livro abrangente sobre arquitetura e casos de uso
- Altinity Blog - ClickHouse Performance Tuning — Artigos práticos sobre otimização de queries e modelagem
- ClickHouse vs Druid vs Pinot: Comparação de Bancos Analíticos — Comparação oficial de características técnicas
- Grafana + ClickHouse: Configuração de Dashboards — Tutorial oficial de integração para visualização em tempo real