Quase todo blog post sobre Kafka na internet conta a mesma história: o que é um producer, o que é um consumer, como criar um tópico, como particionar mensagens. Essa introdução genérica se repete tanto que virou um template.
Aqui, eu quero ir além do que qualquer busca no Google entrega.
Todo mundo repete que “Kafka processa milhões de mensagens por segundo”, que é “resiliente”, que é “a espinha dorsal de pipelines de dados”, mas raramente alguém responde a pergunta que realmente importa para quem constrói sistemas complexos:
Como, exatamente, o Kafka funciona por baixo dos panos para entregar tudo isso?
Como ele escreve tão rápido?
Como ele lê mensagens antigas sem custo linear?
Como ele retém 7 dias ou 14 TB de dados sem degradar?
Como ele limpa dados sem travar o filesystem?
Como ele evita transformar um log de 10 TB em um gargalo de I/O?
Essa é a história que quase ninguém conta.
Este post não é sobre “como usar Kafka”. Este post é sobre como o Kafka é possível é sobre como ele explora as propriedades do sistema operacional, do filesystem e da física dos discos para parecer um “log infinito” de alta performance.
Partições: O coração do Kafka
Por definição, podemos entender as partições no Kafka como a forma de transformar
um tópico lógico em múltiplos logs físicos totalmente independentes, cada um com sua
própria sequência de offsets, seus próprios segmentos e sua própria política de retenção.
É essa divisão granular que permite ao Kafka escalar horizontalmente, garantir ordenação
dentro de cada fluxo e manipular dados no disco sem jamais depender de um arquivo monolítico
gigantesco.
Podemos imaginar partições como trilhos paralelos: cada trilho carrega parte do fluxo, cada um avança no seu próprio ritmo, e juntos formam a ferrovia que permite transportar milhões de mensagens por segundo sem colisão, sem gargalos e sem perder ordenação.
Até aqui, nenhuma novidade é a explicação clássica sobre partições que todo mundo já conhece.
Mas a pergunta que realmente importa é: como o Kafka faz isso funcionar de verdade?
Se tentarmos pensar de maneira ingênua (e eu embarcava nessa resposta até pouco tempo), talvez concluíssemos que para cada partição o Kafka poderia simplesmente criar um arquivo separado no sistema operacional e manter um ponteiro por consumer group ativo. Mas essa abordagem quebra rapidamente: partições com retenção longa e volume alto virariam arquivos monstruosos, difíceis de manipular e impossíveis de truncar com eficiência.
Por quê? Porque deletar o “começo” de um arquivo de 10 TB é inviável. Os sistemas de arquivos não foram projetados para isso.
🔥 Você não consegue remover os primeiros 100MB sem reescrever o restante inteiro.
Para fugir desse colapso inevitável, o Kafka adota uma solução elegantíssima e profundamente inteligente: Segmentação + Rolling.
Segmentos & Rolling
Quando finalmente olhamos para o disco, a abstração cai: cada partição do Kafka não é um arquivo, mas sim uma pasta inteira. Algo assim:
kafka-data/
└── .../my-topic-0/
├── 00000000000000000000.log
├── 00000000000000000000.index
├── 00000000000000001000.log
├── 00000000000000001000.index
└── ...Cada arquivo .log é um Segmento: um bloco imutável do log daquela partição,
com seu próprio .index para permitir buscas eficientes por offset.
É essa divisão em pequenos blocos que evita o pesadelo do “arquivo gigantesco”
e abre caminho para o mecanismo que realmente dá vida ao Kafka.
Podemos dividir a história dos segmentos em quatro partes.
Active Segment
Somente um segmento está aberto para escrita: o active segment. Ele é o trecho “quente” do log, mantido no page cache do sistema operacional. Isso significa que as escritas são absurdamente rápidas porque:
- são sequenciais, não aleatórias
- quase sempre batem no cache do SO
- não existe custo de seek
- a operação é literalmente append
Kafka não tenta reinventar o filesystem. Ele faz o oposto: se apoia diretamente na física do disco e no comportamento do OS para entregar desempenho de milhões de mensagens por segundo.
Nada de random writes. Nada de blocos espalhados. Sempre append. Sempre quente.
Rolling
Mas nada dura para sempre nem mesmo o active segment. Quando ele atinge o limite configurado (por tamanho, como 1GB, ou por tempo, como 7 dias),
o Kafka executa um pequeno ritual que mantém todo o sistema respirando:
-
Fecha o arquivo
O segmento deixa de aceitar novas escritas. Ele se torna um bloco imutável do passado. -
Marca como somente leitura
A partir daqui, esse segmento está congelado. Nenhum byte será alterado.
É um ponto fixo na linha do tempo da partição. -
Cria um novo active segment
Um arquivo novinho em folha nasce para receber todas as escritas futuras: quente, limpo, inteiramente dedicado ao próximo capítulo do log.
Esse mecanismo de rolling é o que impede a catástrofe de um “logzão” infinito.
Em vez disso, o Kafka organiza o histórico em blocos discretos, previsíveis e gerenciáveis,
permitindo que cada pedaço do log viva, envelheça e morra sem jamais comprometer a performance
do sistema como um todo.
Retenção: Delete por Segmento, não por Mensagem
Como entendemos do Acive Segment e Rolling agora ficou facil. Essa é uma das partes mais bonitas (e menos compreendidas) do design do Kafka se o Rolling marca o arquivo como somente leitura após atingir o periodo de retenção configurado, então ele não apaga mensagem por mensagem ele apaga o segmento inteiro.
Para cada segmento fechado, o Kafka pergunta:
“O segmento
000000.logainda contém alguma mensagem dentro da política de retenção?”
A resposta é binária:
- Sim → mantenha o arquivo como está.
- Não → delete o arquivo inteiro.
E aqui entra o truque que transforma o Kafka numa máquina de throughput:
deletar um arquivo no SO é apenas um unlink() que é uma operação O(1).
O sistema operacional simplesmente remove o ponteiro para o arquivo, sem reescrever nada, sem reorganizar blocos, sem mover bytes.
É quase instantâneo.
Esse detalhe físico, essa escolha de modelagem, é um dos motivos pelos quais o Kafka consegue
reter terabytes, apagar terabytes e ainda assim manter escrita e leitura em alta velocidade.
Não há truncamento. Não há poda manual. Há apenas segmentos inteiros sendo reciclados,
como páginas de um diário sendo arrancadas sem esforço.
É assim que o Kafka escala tão absurdamente bem.
Sparse Index: Buscas rápidas sem scan linear
Até aqui entendemos como o Kafka escreve. Mas falta responder uma das perguntas-chave do sistema:
Como o Kafka consegue ler tão rápido, mesmo com dezenas ou centenas de segmentos no disco?
Como ele encontra o offset exato sem varrer tudo?
A resposta está em um mecanismo simples, eficiente e brilhante: o Sparse Index.
Imagine que uma partição tenha 100 segmentos e agora um consumidor pede exatamente o offset 500.000.
Como o Kafka descobre onde isso está?
Cada arquivo .log possui um arquivo irmão .index contendo checkpoints esparsos:
offset -> posição no arquivo
0 -> 0
100 -> 4096
200 -> 8192Não há um mapeamento para cada mensagem, apenas para “saltos” de tamanho fixo (geralmente ~4KB).
A busca acontece assim:
-
Binary search no
.index
O arquivo é pequeno o bastante para caber em RAM.
Encontramos rapidamente o checkpoint mais próximo do offset desejado. -
Seek direto para a posição no
.log
O Kafka salta para a região correta do segmento no disco. -
Leitura linear do trecho restante
Ele só precisa ler alguns poucos registros até encontrar o offset exato.
E, assim como na escrita, a mágica não está em algoritmos complexos. A mágica está em respeitar a física do disco e explorar o que o filesystem faz bem: buscas indexadas + leitura sequencial em blocos contíguos.
Evitando incidentes: Segmentos pequenos demais
Agora que entendemos como o Kafka escreve, rola, indexa e apaga segmentos inteiros, precisamos falar sobre a armadilha que derruba mais clusters do que qualquer outra coisa
Segmentos pequenos demais.
Quando você configura, por exemplo, segment.ms = 5min em um tópico de baixo volume,
o Kafka é obrigado a criar um novo segmento a cada poucos minutos ... mesmo que tenham
pouquíssimas mensagens dentro. O resultado?
Uma explosão de micro-arquivos.
E cada segmento carrega sua tríade:
.log.index.timeindex
Agora imagine centenas, milhares desses trios espalhados pelo disco.
O SO precisa manter cada um deles como file handle aberto. E file handles não são
infinitos ... o limite é o famoso ulimit -n.
Quando esse limite estoura, acontece o inevitável: o broker cai.
Não é mágica. Não é bug. É a consequência direta de um ajuste de configuração fora da realidade.
É por isso que entender a física dos segmentos ... e não apenas a API ... é o que separa operar Kafka com confiança de simplesmente “rodar Kafka”.
Chega de Teoria: Vamos ver acontecer.
Como o objetivo deste artigo não é explicar como subir local um kafka e executar um pipeline, vou deixar isso para outro post. Para facilitar a sua vida eu já subi um repositório do github com toda configuração feita e scripts para executar o pipeline dos conceitos acima. Bora dale?
