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.
| Pergunta | Resposta |
|---|---|
| 1 | B |
| 2 | B |
| 3 | C |
| 4 | C |
| 5 | B |
| 6 | B |
| 7 | B |
| 8 | C |
| 9 | B |
| 10 | C |
| 11 | B |
| 12 | C |
| 13 | B |
| 14 | B |
| 15 | B |
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, comTimestampTimepor ú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 eavgState()é 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 8Principais 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 índicetextmantém uma lista real de postings por token. A taxa de falsos positivos nas consultas é de aproximadamente 0, contra o padrão~0.025do 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
textrecebe 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
LIKElinha 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,hasTokene 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
| Etapa | Elasticsearch | ClickHouse |
|---|---|---|
| 1 | Tokenizar 'timeout' → [timeout] | Igual |
| 2 | Buscar 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 |
| 3 | Obter cada documento correspondente, pontuá-lo e retornar os resultados classificados | Ler 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 dominante | Percorrer a lista de postings | Scan de linhas nos grânulos candidatos (ainda cerca de 24 vezes menos dados do que sem índice) |
Quando cada um é melhor
| Caso de uso | Mais adequado | Motivo |
|---|---|---|
| Pesquisa de produtos em e-commerce ("tênis de corrida por US$ 40–80") | ES | O ranking de relevância é central; as consultas retornam pequenos conjuntos classificados |
| Pesquisa em documentação enquanto se digita | ES | Correspondência aproximada, consultas de frases e preenchimento automático |
| Análise de logs ("erros 5xx que mencionam 'timeout' na última hora") | CH | Filtrar e depois agregar bilhões de linhas; o ranking é irrelevante |
| Análise de segurança ("alertar sobre a string 'malicious_pattern'") | CH | O scan com eliminação é muito adequado |
| "100 logs mais relevantes para uma consulta de texto livre" | ES | O 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" | CH | A 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
LogAttributessem 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ção | Significado |
|---|---|
| 14–15 | Você assimilou a mecânica do ClickHouse e o OTel. Está pronto para conduzir a migração de um cliente. |
| 11–13 | Aprovado — 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–10 | No limite. Releia as respostas-modelo da Parte 2 — esses exercícios abordam diretamente a maioria dos conceitos. |
| ≤ 7 | Refaça os pontos de ensino relevantes antes de tentar as questões de múltipla escolha dos cenários aplicados. |