A ingestão eficiente de dados é a base de implantações do ClickHouse com alto desempenho. Selecionar a estratégia de inserção correta pode afetar drasticamente a taxa de transferência, o custo e a confiabilidade. Esta seção apresenta melhores práticas, trade-offs e opções de configuração para ajudar você a tomar a decisão certa para a sua carga de trabalho.
Inserções síncronas por padrão
Por padrão, as inserções no ClickHouse são síncronas. Cada consulta de inserção cria imediatamente uma parte de armazenamento em disco, incluindo metadados e índices.
Use inserções síncronas se puder agrupar os dados em lotes no clienteCaso contrário, veja Inserções assíncronas abaixo.
A seguir, analisamos brevemente como funcionam as inserções do MergeTree no ClickHouse:
Etapas no lado do cliente
Para obter o melhor desempenho, os dados precisam ser ① agrupados em lotes, o que torna o tamanho do lote a primeira decisão.
O ClickHouse armazena em disco os dados inseridos, ordenados pelas colunas da chave primária da tabela. A segunda decisão é ② pré-ordenar os dados antes de transmiti-los ao servidor. Se um lote chegar pré-ordenado pelas colunas da chave primária, o ClickHouse pode pular a etapa ⑩ de ordenação, acelerando a ingestão.
Se os dados a serem ingeridos não tiverem um formato predefinido, a decisão principal é escolher um formato. O ClickHouse oferece suporte à inserção de dados em mais de 70 formatos. No entanto, ao usar o cliente de linha de comando do ClickHouse ou clientes para linguagens de programação, essa escolha geralmente é feita automaticamente. Se necessário, essa seleção automática também pode ser substituída explicitamente.
A próxima decisão importante é ④ se os dados devem ser compactados antes da transmissão para o servidor ClickHouse. A compactação reduz o volume de dados transferidos e melhora a eficiência da rede, resultando em transferências mais rápidas e menor uso de largura de banda, especialmente para grandes conjuntos de dados.
Os dados são ⑤ transmitidos por uma interface de rede do ClickHouse — seja a interface nativa ou a interface HTTP (que comparamos mais adiante neste post).
Após ⑥ receber os dados, o ClickHouse ⑦ os descomprime, se a compressão tiver sido usada, e então ⑧ os processa a partir do formato originalmente enviado.
Usando os valores desses dados formatados e a instrução DDL da tabela de destino, o ClickHouse ⑨ monta um bloco em memória no formato MergeTree, ⑩ ordena as linhas pelas colunas da chave primária, caso ainda não estejam pré-ordenadas, ⑪ cria um índice primário esparso, ⑫ aplica compressão por coluna e ⑬ grava os dados em disco como uma nova ⑭ parte de dados.
Inserções em lote, se síncronas
Os mecanismos acima ilustram uma sobrecarga constante, independentemente do tamanho da inserção, o que torna o tamanho do lote o fator de otimização mais importante para a taxa de transferência da ingestão. Agrupar inserções em lotes reduz a sobrecarga em relação ao tempo total de inserção e melhora a eficiência do processamento.
Recomendamos inserir dados em lotes de pelo menos 1.000 linhas e, idealmente, entre 10.000 e 100.000 linhas. Fazer menos inserções, porém maiores, reduz o número de partes gravadas, minimiza a carga de merge e diminui o uso geral de recursos do sistema.
Para que uma estratégia de inserção síncrona seja eficaz, esse agrupamento em lotes no lado do cliente é necessário.
Se você não conseguir agrupar os dados em lotes no lado do cliente, o ClickHouse oferece suporte a inserções assíncronas, que transferem esse agrupamento para o servidor (veja Inserções assíncronas).
Independentemente do tamanho das suas inserções, recomendamos manter o número de consultas de inserção em cerca de uma consulta de inserção por segundo. O motivo dessa recomendação é que as partes criadas são mescladas em partes maiores em segundo plano (para otimizar seus dados para consultas de leitura), e enviar consultas de inserção demais por segundo pode levar a situações em que a mesclagem em segundo plano não consegue acompanhar o número de novas partes. No entanto, você pode usar uma taxa maior de consultas de inserção por segundo ao usar inserções assíncronas (veja Inserções assíncronas).
Garanta retentativas idempotentes
As inserções síncronas também são idempotentes. Ao usar motores MergeTree, o ClickHouse deduplicará inserções por padrão. Isso protege contra casos de falha ambíguos, como:
- A inserção foi bem-sucedida, mas o cliente nunca recebeu uma confirmação devido a uma interrupção na rede.
- A inserção falhou no lado do servidor e gerou timeout.
Em ambos os casos, é seguro repetir a inserção — desde que o conteúdo e a ordem do lote permaneçam idênticos. Por esse motivo, é fundamental que os clientes façam retentativas de forma consistente, sem modificar nem reordenar os dados.
Escolha o destino certo para a inserção
Para clusters com shards, você tem duas opções:
- Fazer inserção diretamente em uma tabela MergeTree ou ReplicatedMergeTree. Essa é a opção mais eficiente quando o cliente consegue fazer balanceamento de carga entre os shards. Com
internal_replication = true, o ClickHouse gerencia a replicação de forma transparente.
- Fazer inserção em uma tabela distribuída. Isso permite que os clientes enviem dados para qualquer nó e deixem o ClickHouse encaminhá-los ao shard correto. É mais simples, mas tem desempenho um pouco inferior devido à etapa adicional de encaminhamento.
internal_replication = true continua sendo recomendado.
No ClickHouse Cloud, todos os nós leem e gravam no mesmo shard. As inserções são balanceadas automaticamente entre os nós. Você pode simplesmente enviar as inserções para o endpoint exposto.
Escolher o formato de entrada certo é crucial para uma ingestão eficiente de dados no ClickHouse. Com mais de 70 formatos compatíveis, selecionar a opção de melhor desempenho pode impactar significativamente a velocidade de inserção, o uso de CPU e memória e a eficiência geral do sistema.
Embora a flexibilidade seja útil para engenharia de dados e importações baseadas em arquivos, os aplicativos devem priorizar formatos voltados para desempenho:
- Formato nativo (recomendado): O mais eficiente. Colunar, com necessidade mínima de parsing no lado do servidor. Usado por padrão nos clientes Go e Python.
- RowBinary: Formato eficiente baseado em linhas, ideal se a transformação para o modelo colunar for difícil no lado do cliente. Usado pelo cliente Java.
- JSONEachRow: Fácil de usar, mas caro de parsear. Adequado para casos de uso de baixo volume ou integrações rápidas.
A compactação desempenha um papel fundamental na redução da sobrecarga de rede, na aceleração de inserções e na diminuição dos custos de armazenamento no ClickHouse. Quando usada de forma eficaz, ela melhora o desempenho da ingestão sem exigir mudanças no formato ou no esquema dos dados.
Compactar os dados de inserção reduz o tamanho do payload enviado pela rede, minimizando o consumo de largura de banda e acelerando a transmissão.
Para inserções, a compactação é especialmente eficaz quando usada com o formato Native, que já corresponde ao modelo interno de armazenamento colunar do ClickHouse. Nessa configuração, o servidor pode descompactar e armazenar os dados diretamente, com o mínimo de transformação.
Use LZ4 para velocidade, ZSTD para taxa de compressão
O ClickHouse oferece suporte a vários codecs de compressão durante a transmissão de dados. Duas opções comuns são:
- LZ4: Rápido e leve. Reduz significativamente o tamanho dos dados com sobrecarga mínima de CPU, sendo ideal para inserções com alto throughput e o padrão na maioria dos clientes ClickHouse.
- ZSTD: Maior taxa de compressão, mas com uso mais intenso de CPU. É útil quando os custos de transferência de rede são altos — como em cenários entre regiões ou entre provedores de Cloud — embora aumente ligeiramente a capacidade de processamento no lado do cliente e o tempo de descompressão no lado do servidor.
Boa prática: use LZ4, a menos que você tenha largura de banda limitada ou incorra em custos de egresso de dados — nesse caso, considere ZSTD.
Em testes do benchmark FastFormats, inserções Native comprimidas com LZ4 reduziram o tamanho dos dados em mais de 50%, diminuindo o tempo de ingestão de 150s para 131s em um conjunto de dados de 5,6 GiB. Ao mudar para ZSTD, o mesmo conjunto de dados foi comprimido para 1,69 GiB, mas o tempo de processamento no lado do servidor aumentou ligeiramente.
A compressão reduz o uso de recursos
A compressão não só reduz o tráfego de rede como também melhora a eficiência de CPU e memória no servidor. Com dados comprimidos, o ClickHouse recebe menos bytes e gasta menos tempo analisando entradas grandes. Esse benefício é especialmente importante na ingestão feita por vários clientes simultâneos, como em cenários de observabilidade.
O impacto da compressão sobre CPU e memória é modesto com LZ4 e moderado com ZSTD. Mesmo sob carga, a eficiência no lado do servidor melhora devido ao menor volume de dados.
Combinar compressão com envio em lotes e um formato de entrada eficiente (como Native) oferece o melhor desempenho de ingestão.
Ao usar a interface nativa (por exemplo, clickhouse-client), a compressão LZ4 vem habilitada por padrão. Se quiser, você pode mudar para ZSTD nas configurações.
Com a interface HTTP, use o cabeçalho Content-Encoding para aplicar compressão (por exemplo, Content-Encoding: lz4). Todo o payload deve ser comprimido antes do envio.
Pré-ordene se o custo for baixo
Pré-ordenar os dados pela chave primária antes da inserção pode melhorar a eficiência da ingestão no ClickHouse, principalmente em lotes grandes.
Quando os dados chegam pré-ordenados, o ClickHouse pode ignorar ou simplificar a etapa interna de ordenação durante a criação das partes, reduzindo o uso de CPU e acelerando o processo de inserção. A pré-ordenação também melhora a eficiência da compressão, já que valores semelhantes ficam agrupados — permitindo que codecs como LZ4 ou ZSTD alcancem uma taxa de compressão melhor. Isso é especialmente vantajoso quando combinado com inserções em lotes grandes e compressão, pois reduz tanto a sobrecarga de processamento quanto a quantidade de dados transferidos.
Dito isso, a pré-ordenação é uma otimização opcional, não um requisito. O ClickHouse ordena os dados com alta eficiência usando processamento paralelo e, em muitos casos, ordenar no servidor é mais rápido ou mais conveniente do que pré-ordenar no cliente.
Recomendamos a pré-ordenação apenas se os dados já estiverem quase ordenados ou se os recursos do cliente (CPU, memória) forem suficientes e estiverem subutilizados. Em casos de uso sensíveis à latência ou de alto throughput, como observabilidade, em que os dados chegam fora de ordem ou a partir de muitos agentes, geralmente é melhor pular a pré-ordenação e confiar no desempenho nativo do ClickHouse.
As inserções assíncronas no ClickHouse oferecem uma alternativa poderosa quando o batching no lado do cliente não é viável. Isso é especialmente valioso em cargas de trabalho de observabilidade, nas quais centenas ou milhares de agentes enviam dados continuamente — logs, métricas, traces — muitas vezes em payloads pequenos e em tempo real. Manter dados em buffer no lado do cliente nesses ambientes aumenta a complexidade, exigindo uma fila centralizada para garantir o envio de batches grandes o suficiente.
Não é recomendável enviar muitos batches pequenos no modo síncrono, pois isso leva à criação de muitas partes. Isso resultará em baixo desempenho de consulta e em erros de “too many part”.
As inserções assíncronas transferem a responsabilidade pelo batching do cliente para o servidor, gravando os dados recebidos em um buffer na memória e depois descarregando-os para o armazenamento com base em limites configuráveis. Essa abordagem reduz significativamente a sobrecarga de criação de partes, diminui o uso de CPU e garante que a ingestão permaneça eficiente — mesmo sob alta concorrência.
O comportamento principal é controlado pela configuração async_insert.
As inserções assíncronas têm suporte nas interfaces HTTP e TCP nativo.
Quando habilitadas (async_insert = 1), as inserções são mantidas em buffer e gravadas em disco somente quando uma das condições de flush é atendida:
O primeiro limite atingido aciona o flush.
Esse processo de batching é invisível para os clientes e ajuda o ClickHouse a mesclar com eficiência o tráfego de inserções de várias fontes. No entanto, até que ocorra um flush, os dados não podem ser consultados. É importante destacar que há vários buffers para cada combinação de formato de insert e configurações e, em clusters, os buffers são mantidos por nó, o que possibilita um controle granular em ambientes multilocatários. Fora isso, a mecânica de inserção é idêntica à descrita para inserções síncronas.
Escolhendo um modo de retorno
O comportamento das inserções assíncronas é refinado ainda mais com a configuração wait_for_async_insert.
Quando definida como 1 (o padrão), o ClickHouse só confirma a inserção depois que os dados são gravados com sucesso em disco. Isso garante fortes garantias de durabilidade e torna o tratamento de erros simples: se algo der errado durante o flush, o erro é retornado ao cliente. Esse modo é recomendado para a maioria dos cenários de produção, especialmente quando falhas de inserção precisam ser rastreadas com confiabilidade.
Benchmarks mostram que isso escala bem com a concorrência — esteja você executando 200 ou 500 clientes — graças às inserções adaptativas e ao comportamento estável na criação de partes.
Definir wait_for_async_insert = 0 habilita o modo “fire-and-forget”. Nesse caso, o servidor confirma a inserção assim que os dados são colocados no buffer, sem esperar que cheguem ao armazenamento.
Isso oferece inserções com latência ultrabaixa e throughput máximo, ideal para dados de alta velocidade e baixa criticidade. No entanto, isso traz desvantagens: não há garantia de que os dados serão persistidos, os erros só aparecem durante o flush, e não há fila de dead-letter para inserções com falha — rastrear falhas exige inspecionar os logs do servidor e as tabelas do sistema posteriormente. Use esse modo apenas se o seu workload puder tolerar perda de dados.
Benchmarks também demonstram redução substancial de partes e menor uso de CPU quando os flushes do buffer são pouco frequentes (por exemplo, a cada 30 segundos), mas o risco de falha silenciosa permanece.
Nossa recomendação enfática é usar async_insert=1,wait_for_async_insert=1 ao usar inserções assíncronas. Usar wait_for_async_insert=0 é muito arriscado porque seu cliente de INSERT pode não perceber se houver erros, e também pode causar uma possível sobrecarga se o cliente continuar gravando rapidamente em uma situação em que o servidor ClickHouse precise desacelerar as gravações e criar alguma contrapressão para garantir a confiabilidade do serviço.
Inserts assíncronos adaptativos
Desde a versão 24.2, o ClickHouse usa timeouts adaptativos de flush por padrão (async_insert_use_adaptive_busy_timeout). Em vez de um intervalo de flush fixo, o timeout se ajusta dinamicamente entre um mínimo (async_insert_busy_timeout_min_ms, padrão de 50 ms) e um máximo (async_insert_busy_timeout_max_ms, padrão de 200 ms ou 1000 ms no Cloud), com base na taxa de chegada dos dados.
Quando os dados chegam com frequência, o timeout fica mais próximo do mínimo para fazer o flush mais cedo e reduzir a latência de ponta a ponta. Quando os dados são esparsos, ele aumenta em direção ao máximo para acumular lotes maiores. Isso é especialmente útil no modo padrão (wait_for_async_insert=1), em que um timeout fixo alto faria os clientes ficarem bloqueados durante todo o intervalo, mesmo quando os dados já estivessem prontos para flush.
A validação do schema e o parsing dos dados acontecem durante o flush do buffer, não quando o insert é recebido. Se qualquer linha em uma consulta de insert tiver erro de parsing ou de tipo, nenhum dado dessa consulta é gravado — todo o payload da consulta é rejeitado. No modo padrão (wait_for_async_insert=1), o erro é retornado ao cliente. No modo fire-and-forget, os erros são gravados nos logs do servidor e na tabela system.asynchronous_inserts.
Cada flush cria pelo menos uma parte para cada valor distinto da chave de particionamento no buffer. Mesmo em tabelas sem chave de particionamento, um único flush pode produzir várias partes se os dados em buffer excederem max_insert_block_size (padrão: ~1 milhão de linhas).
Apesar de usar async inserts, você ainda pode encontrar erros de “too many parts” se a chave de particionamento tiver alta cardinalidade.
Desduplicação e confiabilidade
Por padrão, o ClickHouse realiza desduplicação automática em inserções síncronas, o que torna as tentativas de repetição seguras em cenários de falha. No entanto, isso fica desabilitado para inserções assíncronas, a menos que seja explicitamente habilitado (isso não deve ser habilitado se você tiver visões materializadas dependentes — veja a issue).
Na prática, se a desduplicação estiver ativada e a mesma inserção for repetida — por exemplo, devido a um timeout ou a uma interrupção de rede — o ClickHouse pode ignorar a duplicata com segurança. Isso ajuda a manter a idempotência e evita a gravação duplicada de dados.
Habilitando inserções assíncronas
As inserções assíncronas podem ser habilitadas para um determinado usuário ou para uma consulta específica:
-
Habilite as inserções assíncronas no nível do usuário. Este exemplo usa o usuário
default; se você criar outro usuário, substitua pelo respectivo nome de usuário:
-
Você pode especificar as configurações de inserção assíncrona usando a cláusula SETTINGS nas consultas de inserção:
-
Você também pode especificar as configurações de inserção assíncrona como parâmetros de conexão ao usar um cliente ClickHouse de uma linguagem de programação.
Por exemplo, veja como fazer isso em uma string de conexão JDBC ao usar o driver JDBC Java do ClickHouse para se conectar ao ClickHouse Cloud:
As inserções assíncronas não se aplicam a consultas INSERT INTO ... SELECT. Quando a inserção contém uma cláusula SELECT, a consulta é sempre executada de forma síncrona, independentemente da configuração async_insert.
Esvaziando os buffers ao encerrar
Para esvaziar todos os buffers pendentes de async insert — por exemplo, durante um encerramento controlado ou antes de uma manutenção — execute:
Isso garante que todos os dados em buffer sejam gravados no armazenamento antes que o servidor pare.
As inserções assíncronas são o substituto moderno das tabelas Buffer. Principais diferenças:
- Nenhuma alteração de DDL é necessária. As inserções assíncronas são transparentes — você habilita uma configuração, sem criar tabelas adicionais.
- Buffer por formato de consulta. As inserções assíncronas mantêm buffers separados para cada combinação única de formato de consulta e configurações, permitindo políticas de flush mais granulares. As tabelas Buffer usam um único buffer por tabela de destino.
- Durabilidade. No modo padrão (
wait_for_async_insert=1), os dados são confirmados em disco antes de o cliente receber a confirmação. As tabelas Buffer funcionam no estilo fire-and-forget — os dados em buffer são perdidos em caso de falha.
- Comportamento em cluster. Em clusters, os buffers de inserção assíncrona são mantidos por nó. As tabelas Buffer exigem criação explícita em cada nó.
Escolha uma interface: HTTP ou nativa
O ClickHouse oferece duas interfaces principais para ingestão de dados: a interface nativa e a interface HTTP — cada uma com seus próprios trade-offs entre desempenho e flexibilidade. A interface nativa, usada pelo clickhouse-client e por alguns clientes de linguagem, como Go e C++, foi projetada especificamente para desempenho. Ela sempre transmite dados no formato Native, altamente eficiente, do ClickHouse, oferece suporte à compressão em blocos com LZ4 ou ZSTD e minimiza o processamento no lado do servidor ao transferir para o cliente tarefas como parsing e conversão de formato.
Ela ainda permite calcular, no lado do cliente, os valores de colunas MATERIALIZED e DEFAULT, fazendo com que o servidor possa pular essas etapas por completo. Isso torna a interface nativa ideal para cenários de ingestão com alto throughput em que a eficiência é crítica.
Ao contrário de muitos bancos de dados tradicionais, o ClickHouse também oferece uma interface HTTP. Ela, por sua vez, prioriza compatibilidade e flexibilidade. Ela permite enviar dados em qualquer formato compatível — incluindo JSON, CSV, Parquet e outros — e tem amplo suporte na maioria dos clientes ClickHouse, incluindo Python, Java, JavaScript e Rust.
Isso costuma ser preferível ao protocolo nativo do ClickHouse, pois permite que o tráfego seja redirecionado facilmente por balanceadores de carga. Esperamos pequenas diferenças no desempenho de inserção em relação ao protocolo nativo, que tem um pouco menos de sobrecarga.
No entanto, ela não oferece a integração mais profunda do protocolo nativo e não pode realizar otimizações no lado do cliente, como o cálculo de valores materializados ou a conversão automática para o formato Native. Embora inserções via HTTP ainda possam ser compactadas usando headers HTTP padrão (por exemplo, Content-Encoding: lz4), a compressão é aplicada ao payload inteiro, e não a blocos de dados individuais. Essa interface costuma ser preferida em ambientes nos quais simplicidade de protocolo, balanceamento de carga ou ampla compatibilidade de formatos são mais importantes do que desempenho bruto.
Para uma descrição mais detalhada dessas interfaces, consulte aqui.