Elasticsearch MigrationClickHouse Workshops

Gabarito das questões de múltipla escolha

Respostas e explicações das quinze verificações de conhecimento sobre migração.

Use primeiro a avaliação interativa da Parte 4. Ela revela a resposta correta e a explicação somente depois que as 15 perguntas forem enviadas. Esta referência mantém as notas técnicas mais detalhadas para revisão posterior.

PerguntaResposta
1B
2B
3C
4C
5B
6B
7B
8C
9B
10C
11B
12C
13B
14B
15B

P1. Correspondência do prefixo da chave de ordenação — B

O índice primário do ClickHouse é esparso — uma entrada para cada index_granularity linhas (8192 por padrão). O mecanismo usa o índice para ignorar grânulos que não podem conter correspondências com base nas primeiras colunas da chave de ordenação. Uma consulta que filtra por:

  • O prefixo completo (ServiceName, SeverityText, TimestampTime) — as três colunas da chave de ordenação, com TimestampTime por último (a coluna final de intervalo) — dá ao índice a capacidade máxima de ignorar grânulos.

A filtra apenas pela coluna final — o mecanismo pode usar o mínimo/máximo por grânulo de TimestampTime, mas não consegue aproveitar plenamente o índice esparso, pois as primeiras colunas não estão restringidas. C usa LIKE em uma coluna que não está na chave de ordenação — exige um índice de skipping (por exemplo, tokenbf_v1) ou um scan completo. D filtra por uma coluna que nem sequer está na chave de ordenação.

Modelo mental: "Quanto do prefixo da chave de ordenação meu predicado restringe?"


P2. Granularidade do descarte por TTL — B

SETTINGS ttl_only_drop_parts = 1 é o segredo. Com essa configuração, o ClickHouse espera até que uma parte MergeTree inteira expire (ou seja, até que seu TimestampDate máximo ultrapasse INTERVAL 30 DAY) e então desvincula toda a parte no disco. Isso é muito mais barato do que o comportamento padrão (reescrever a parte sem as linhas expiradas) e combina naturalmente com o particionamento por data, no qual cada partição fica em poucas partes.

A descreve o comportamento padrão do TTL sem ttl_only_drop_parts = 1. C aconteceria apenas com DROP TABLE. D não é um modo real do ClickHouse.

Modelo mental: O TTL de séries temporais deve descartar partes, não linhas — verifique se a chave de partição está alinhada ao limite do TTL.


P3. Aplicabilidade de LowCardinality — C

LowCardinality(String) cria um dicionário local da coluna que mapeia cada string distinta para um pequeno código inteiro e armazena esses códigos. A compactação é excelente quando há pouquíssimos valores distintos (como ServiceName, SeverityText, Region, EnvironmentName). O ponto de equilíbrio observado é de cerca de 10.000 valores distintos; acima disso, o próprio dicionário começa a dominar o custo de armazenamento e consulta.

A anularia totalmente o benefício do dicionário (cada linha receberia um novo código). B é um caso clássico em que se deseja armazenamento de texto completo com um índice de skipping. D se aplica a Date/DateTime, não a strings — e os tipos temporais nativos do ClickHouse já são eficientes.


P4. Finalidade do mecanismo Null — C

O padrão otel_logs (Null engine) → otel_logs_mv → otel_logs_v2 (MergeTree) é um uso clássico do ClickHouse: ele permite aceitar inserções compatíveis com o esquema OTel bruto (que o OTel Collector sabe gravar), persistindo somente o esquema transformado que realmente será consultado (com colunas materializadas para GeoCountry, BrowserFamily, RequestPage etc.). Sem a tabela Null, seria necessário (a) armazenar os dados duas vezes ou (b) fazer o OTel Collector conhecer seu esquema de destino personalizado, perdendo a característica plug-and-play.

A/B/D não são o motivo. O Collector pode gravar diretamente no MergeTree — mas isso acopla o esquema do collector ao esquema de armazenamento, exatamente o que queremos evitar.


P5. Onde ficam os atributos no OTel — B

O modelo de dados do OpenTelemetry divide os atributos em:

  • Atributos de recurso — descrevem a entidade que produz telemetria (host, contêiner, serviço, pod do k8s). São definidos uma vez na inicialização do SDK e propagados automaticamente.
  • Atributos de span/log/métrica — descrevem um evento específico. Variam de acordo com o registro do sinal.

service.name, service.version, host.name, cloud.region, k8s.pod.name são todos atributos de recurso. A coluna ServiceName do laboratório é uma promoção de nível superior de ResourceAttributes['service.name'], para que possa ser a primeira coluna da chave de ordenação.


P6. Índices de skipping para filtros que não usam o prefixo — B

Um índice de skipping complementa a chave primária com indicações por grânulo de que "este grânulo não pode conter uma correspondência" para colunas que não estão na chave de ordenação. bloom_filter(0.01) é o padrão para colunas de alta cardinalidade consultadas por igualdade (TraceId, UserId e RequestId).

A na verdade prejudicaria as consultas — grânulos maiores significam que cada descarte representa um erro maior. C é impossível sem reconstruir toda a tabela (ORDER BY é imutável no MergeTree). D não altera os planos de consulta.

Modelo mental: "Chave de ordenação para o padrão de acesso dominante; índices de skipping para os demais."


P7. Por que fazer gravação dupla no collector — B

Quando os dois backends são alimentados pelo mesmo gravador (o collector emite lotes idênticos para o ES e o CH), as comparações de paridade refletem apenas diferenças de armazenamento/mecanismo de consulta. Se, em vez disso, o dashboard fizer leitura dupla, será necessário integrar uma orquestração de consultas entre sistemas; não será possível saber se as diferenças vêm do caminho de armazenamento ou da própria lógica do dashboard; e o rollback será mais complexo (será preciso reverter o código do dashboard, e não apenas remover um exporter do collector).

A é irrelevante. C é falsa (Grafana, HyperDX e até o Kibana por meio de clusters remotos podem ler várias fontes). D está simplesmente errada.


P8. Finalidade do AggregatingMergeTree — C

Vale a pena compreender esta resposta profundamente, pois é aqui que o ClickHouse supera de forma decisiva o padrão de transformações do Elasticsearch.

Nas transformações do Elasticsearch, eventos brutos são examinados periodicamente e reagregados em um índice de resumo. O custo cresce com o volume bruto.

No AggregatingMergeTree do ClickHouse:

  • Cada inserção produz um pequeno estado parcial — por exemplo, countState() é apenas um inteiro e avgState() é um par (soma, contagem).
  • Mesclagens em segundo plano combinam os estados parciais das partes adjacentes (a propriedade algébrica de contagem, soma etc. torna isso trivial; até sketches de quantis, como t-digest, podem ser mesclados).
  • As consultas combinam os estados parciais restantes com agregações *Merge.

As linhas brutas nunca são relidas depois que o estado inicial é calculado. O custo do rollup é pago na inserção, não na consulta, e não cresce com a retenção.


P9. Por que os dicionários IP_TRIE são rápidos — B

Os dicionários no ClickHouse são componentes de primeira classe que ficam na memória do collector após SYSTEM RELOAD DICTIONARY. O layout IP_TRIE é especificamente uma árvore radix (uma trie indexada pelo prefixo de bits), portanto uma consulta CIDR é O(prefix-length) — cerca de 32 comparações de bits para IPv4. Não há disco nem scan, e o único custo por linha é percorrer a trie.

A está errada (os dicionários são expostos pelo planejador; eles apenas têm caminhos rápidos especializados). C é implausível para conjuntos de dados do tamanho do MaxMind (cerca de 3 MB para o banco de países). D é verdadeira, mas não explica a velocidade.


P10. Equivalência do ILM — C

A arquitetura de armazenamento do ClickHouse Cloud elimina a maior parte do ILM:

  • Sem camadas de nós — todos os dados ficam em armazenamento de objetos com cache local automático. Não há alocação "hot versus warm" para gerenciar.
  • Sem rollover — uma única tabela MergeTree particionada por data substitui o padrão de índices rotativos.
  • Sem agendamento de forcemerge — as mesclagens em segundo plano são contínuas e autoajustáveis.
  • O TTL cuida da exclusão — uma cláusula substitui toda a fase de exclusão após 30 dias.

Este é um dos argumentos de maior impacto em qualquer conversa sobre ES → CH. A complexidade operacional do cliente cai em uma ordem de grandeza.


P11. Por que filtrar por SpanKind = 'Server' — B

Esta foi a armadilha encontrada no Exercício 1. Os spans EventStream de long polling de flagd no OTel Demo duram cerca de 10 minutos por definição — eles registram conexões de streaming, não requisições voltadas ao usuário. Eles dominam qualquer consulta ingênua dos "10 mais lentos".

Em produção, você verá o mesmo padrão em qualquer sistema com RPCs de longa duração (SignalR, streaming de servidor gRPC, assinaturas GraphQL e keep-alives MQTT). Ao buscar latência que afeta usuários, sempre filtre por SpanKind = 'Server' (requisições recebidas) ou por um padrão específico de SpanName.

Modelo mental: Spans do tipo Server = "aquilo que o usuário esperou". Internal/Client/Producer/Consumer = "o que aconteceu nos bastidores".


P12. Indexação para pesquisa de texto completo — C

O índice text é a abordagem moderna e recomendada. Ele se tornou disponível de forma geral no ClickHouse 26.2 e é usado pelo próprio esquema otel_logs_v2 do laboratório na coluna Body:

INDEX idx_body Body TYPE text(tokenizer='sparseGrams') GRANULARITY 8

Principais propriedades de text:

  • Tokenizador configurável — tokens (espaços em branco), ngrams(N) (substring), sparseGrams (tokens inteligentes de tamanho variável, padrão do laboratório) ou uma expressão regular personalizada.
  • Precisão melhor do que tokenbf_v1 — em vez da aproximação do filtro Bloom por grânulo, o índice text mantém uma lista real de postings por token. A taxa de falsos positivos nas consultas é de aproximadamente 0, contra o padrão ~0.025 do tokenbf_v1.
  • Tratamento adequado de maiúsculas e minúsculas e suporte a CJK nos tokenizadores mais recentes — o tokenbf_v1 não considerava a segmentação Unicode.

B (tokenbf_v1) ainda funciona e é a resposta correta em versões anteriores à 26.2 do ClickHouse — a pergunta solicita deliberadamente a recomendação moderna. Muitas implantações de produção ainda usam tokenbf_v1; os dois índices podem coexistir na mesma coluna durante um rollover. A (minmax) acompanha o mínimo/máximo por grânulo — útil para colunas numéricas/de data monotônicas, não para texto. D (bloom_filter sem tokenização) trata a string inteira como um token; ele ajuda apenas na igualdade da string inteira (por exemplo, WHERE Body = 'an exact full string'), nunca na correspondência de substrings.

Modelo mental: para substring/texto completo em colunas String: text na versão 26.2 ou superior, tokenbf_v1 em um cluster mais antigo. Para igualdade exata em uma coluna string de alta cardinalidade (por exemplo, TraceId): bloom_filter.


P13. Por que o ClickHouse não inclui um índice invertido no estilo do ES — B

O enquadramento correto é "decisão de projeto diferente", não "recurso ausente". Os dois mecanismos aceitam predicados de texto completo; eles são otimizados para padrões de acesso distintos.

Índice invertido do Elasticsearch

Baseado no Lucene, ele é o caminho de acesso principal para texto:

  • Granularidade: por documento. O índice armazena token → posting list of (doc_id, term_frequency, positions) para cada documento que contém o token.
  • Finalidade: localizar documentos que correspondam a uma consulta. Retorna os documentos realmente correspondentes, não um conjunto de candidatos.
  • Ranking integrado: pontuação BM25/TF-IDF durante a busca; os resultados já vêm classificados por relevância.
  • Obrigatório por padrão: todo campo text recebe um — não é possível ter um campo de texto sem pagar o custo do índice.
  • Custo de armazenamento: normalmente 30–50% do tamanho do documento original.
  • Semântica de consulta rica: frases com slop, correspondência aproximada com distância de Levenshtein, boosting de vários campos e DSL de pontuação — tudo possibilitado pela granularidade por documento.

Índice text do ClickHouse (disponível de forma geral na 26.2)

Um índice de skipping sobre o armazenamento colunar; não é o caminho de acesso principal:

  • Granularidade: por grânulo (8192 linhas por padrão). Informa ao mecanismo que "este grânulo pode conter linhas com o token X" — não identifica as linhas específicas correspondentes.
  • Finalidade: eliminar grânulos de um scan. Depois da eliminação, o ClickHouse lê os grânulos candidatos e aplica o predicado LIKE linha por linha.
  • Sem ranking de relevância. Os resultados são retornados na ordem do armazenamento. Não há BM25, TF-IDF nem "mais relevante do que".
  • Opcional: DDL com adesão explícita em uma coluna. As tabelas sem ele simplesmente examinam a coluna.
  • Custo de armazenamento: cerca de 1–5% do tamanho da coluna — uma lista de postings por grânulo, não por linha.
  • Semântica de consulta: SQL LIKE, ILIKE, hasToken e regex. Sem frases com slop, correspondência aproximada ou DSL de pontuação.

Execução de WHERE Body LIKE '%timeout%' em 1 bilhão de linhas

EtapaElasticsearchClickHouse
1Tokenizar 'timeout' → [timeout]Igual
2Buscar o token no índice invertido → lista de postings dos IDs dos documentos (digamos 2,3 milhões)Percorrer o índice de skipping — digamos que 5 mil dos 122 mil grânulos possam conter timeout; ignorar os outros 117 mil
3Obter cada documento correspondente, pontuá-lo e retornar os resultados classificadosLer as colunas dos 5 mil grânulos candidatos (cerca de 41 milhões de linhas)
4(nenhuma)Aplicar LIKE '%timeout%' linha por linha e obter cerca de 2,3 milhões de correspondências
Custo dominantePercorrer a lista de postingsScan de linhas nos grânulos candidatos (ainda cerca de 24 vezes menos dados do que sem índice)

Quando cada um é melhor

Caso de usoMais adequadoMotivo
Pesquisa de produtos em e-commerce ("tênis de corrida por US$ 40–80")ESO ranking de relevância é central; as consultas retornam pequenos conjuntos classificados
Pesquisa em documentação enquanto se digitaESCorrespondência aproximada, consultas de frases e preenchimento automático
Análise de logs ("erros 5xx que mencionam 'timeout' na última hora")CHFiltrar e depois agregar bilhões de linhas; o ranking é irrelevante
Análise de segurança ("alertar sobre a string 'malicious_pattern'")CHO scan com eliminação é muito adequado
"100 logs mais relevantes para uma consulta de texto livre"ESO CH não tem pontuação — seria necessário escrever seu próprio SQL de ranking
"Somar bytes_transferred quando o caminho corresponder a /api/.* durante 90 dias"CHA agregação domina; o predicado de texto é apenas um filtro

Resumo para a conversa com o cliente

Os dois sistemas conseguem localizar linhas que contêm 'timeout'. A próxima pergunta revela a diferença da decisão de projeto:

  • Depois que o ES encontra correspondências, o passo seguinte natural é "classificar por relevância".
  • Depois que o ClickHouse encontra correspondências, o passo seguinte natural é "agora calcular a latência p99 agrupada por serviço para estas linhas".

O mesmo layout físico de dados não pode ser ideal para as duas perguntas. O ES escolhe orientação a documentos + ranking; o ClickHouse escolhe armazenamento colunar + análise.


P14. Estratégia de evolução do esquema — B

Isto vem diretamente da resposta-modelo da Decisão 3 do ADR da Parte 2:

  • Map por padrão preserva a flexibilidade — qualquer nova chave de atributo simplesmente chega a LogAttributes sem DDL.
  • Promova chaves frequentes a colunas dedicadas quando o uso justificar a alteração do esquema (regra geral: ≥30% das linhas fazem referência à chave e ≥2 dashboards/alertas a utilizam).
  • A promoção não é destrutiva: ALTER TABLE ... ADD COLUMN MATERIALIZED LogAttributes['X'] permite que linhas novas e antigas coexistam.

A (coluna JSON sem promoção) oferece flexibilidade, mas nenhum caminho rápido; os benchmarks mostram desempenho cerca de 2 vezes mais lento do que Map no volume deste laboratório. C ("uma coluna por atributo possível") falha assim que um serviço emite um novo atributo. D não é uma migração.


P15. Trade-off da amostragem — B

A amostragem baseada na cauda adia a decisão de manter/descartar até que todos os spans de um trace sejam recebidos; assim, pode usar sinais no nível do trace (algum span de erro? algum span de alta latência?) para decidir. O tail_sampling_processor do OpenTelemetry é a implementação canônica; o custo está no buffering e na complexidade do collector.

A (baseada no início) decide no começo do trace, antes de qualquer erro ocorrer — incompatível com "manter todos os traces de erro". C (por span) quebra a coerência do trace (você perderia metade dos spans de um trace). D (sem amostragem) raramente é viável com 1 milhão de traces/s.

A decisão de projeto correta em produção normalmente é um amostrador de cauda com regras como "manter 100% dos traces de erro, 5% dos traces lentos, mas sem erro, e 0,1% dos traces rápidos sem erro".


Orientação sobre a pontuação

PontuaçãoSignificado
14–15Você assimilou a mecânica do ClickHouse e o OTel. Está pronto para conduzir a migração de um cliente.
11–13Aprovado — mas releia as explicações das perguntas que errou. As perguntas sobre esquema e modelo de dados são as que melhor predizem o sucesso em conversas com clientes.
8–10No limite. Releia as respostas-modelo da Parte 2 — esses exercícios abordam diretamente a maioria dos conceitos.
≤ 7Refaça os pontos de ensino relevantes antes de tentar as questões de múltipla escolha dos cenários aplicados.

Nesta página

PT