Elasticsearch MigrationClickHouse Workshops

Soluciones de opción múltiple

Respuestas y explicaciones de las quince comprobaciones de conocimientos sobre migración.

Primero realiza la evaluación interactiva de la Parte 4. Solo revela respuestas y explicaciones tras enviar las 15 preguntas. Esta referencia conserva las notas técnicas extensas.

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

P1. Coincidencia con el prefijo de ordenación — B

El índice primario de ClickHouse es disperso: una entrada por cada index_granularity filas (8192 por defecto). Omite gránulos que no pueden coincidir según las primeras columnas de la clave. Filtrar por:

  • El prefijo completo (ServiceName, SeverityText, TimestampTime), con las tres columnas y TimestampTime como rango final, maximiza la poda.

A solo filtra la columna final; puede usar min/max por gránulo, pero no todo el índice disperso. C usa LIKE en una columna ajena y necesita un índice como tokenbf_v1 o una exploración. D tampoco está en la clave. La columna temporal a la que se refiere el ejemplo es TimestampTime.

Modelo mental: «¿Cuánto del prefijo de la clave restringe mi predicado?»


P2. Granularidad de eliminación TTL — B

SETTINGS ttl_only_drop_parts = 1 hace que ClickHouse espere hasta caducar una parte MergeTree completa (su máximo TimestampDate cruza INTERVAL 30 DAY) y la desvincule del disco. Es mucho más barato que reescribir la parte sin filas caducadas y encaja con particiones por fecha.

A describe el comportamiento predeterminado sin ttl_only_drop_parts = 1. C solo sucede con DROP TABLE. D no existe.

Modelo mental: En series temporales, TTL debe eliminar partes, no filas; alinea partición y TTL.


P3. Uso de LowCardinality — C

LowCardinality(String) crea un diccionario local que asigna cada string a un entero. Comprime muy bien con pocos valores (ServiceName, SeverityText, Region, EnvironmentName). El equilibrio está cerca de ~10 000 valores; después el diccionario domina el coste.

A anularía la ventaja: cada fila tendría código nuevo. B necesita texto completo + índice de salto. D corresponde a Date/DateTime, cuyos tipos nativos ya son eficientes.


P4. Finalidad del motor Null — C

El patrón otel_logs (Null engine) → otel_logs_mv → otel_logs_v2 (MergeTree) acepta inserciones con el esquema OTel bruto conocido por el collector y persiste solo el esquema transformado (GeoCountry, BrowserFamily, RequestPage, etc.). Sin Null, habría que almacenar dos veces o acoplar el collector al esquema personalizado.

A/B/D no son el motivo. El collector puede escribir MergeTree directamente, pero acoplaría ingesta y almacenamiento.


P5. Dónde viven los atributos en OTel — B

OpenTelemetry separa:

  • Atributos de recurso: describen la entidad (host, contenedor, servicio, pod); se definen al iniciar el SDK y se propagan.
  • Atributos de span/log/métrica: describen un evento y varían por registro.

service.name, service.version, host.name, cloud.region, k8s.pod.name son recursos. ServiceName promueve ResourceAttributes['service.name'] para ser la primera columna de ordenación.


P6. Índices de salto para filtros sin prefijo — B

Un índice de salto aporta pistas por gránulo para columnas fuera de la clave. bloom_filter(0.01) es el estándar para búsquedas de igualdad de alta cardinalidad (TraceId, UserId, RequestId).

A empeora búsquedas: gránulos mayores significan fallos mayores. C exige reconstruir la tabla (ORDER BY es inmutable en MergeTree). D no cambia planes.

Modelo mental: «Clave de ordenación para el patrón dominante; índices de salto para el resto».


P7. Por qué escribir en ambos destinos desde el collector — B

Si el mismo escritor envía lotes idénticos a ES y CH, las diferencias de paridad pertenecen al almacenamiento o al motor. Leer ambos desde el dashboard introduce orquestación entre sistemas, confunde el origen de las diferencias y complica la reversión: habría que revertir código del dashboard, no quitar un exportador.

A es irrelevante. C es falso: Grafana, HyperDX e incluso Kibana pueden leer varias fuentes. D es incorrecto.


P8. Finalidad de AggregatingMergeTree — C

Aquí ClickHouse supera claramente las transformaciones de Elasticsearch, que exploran periódicamente eventos brutos y vuelven a agregarlos; el coste crece con el volumen.

En AggregatingMergeTree:

  • Cada inserción genera un estado parcial: countState() es un entero; avgState() es (suma, recuento).
  • Los merges combinan estados adyacentes; incluso sketches como t-digest se pueden combinar.
  • Las consultas usan agregados *Merge.

Las filas brutas nunca se releen después de calcular el estado. El coste se paga al insertar y no crece con la retención.


P9. Por qué los diccionarios IP_TRIE son rápidos — B

Los diccionarios viven en memoria tras SYSTEM RELOAD DICTIONARY. IP_TRIE es un árbol radix por prefijo, así que una búsqueda CIDR cuesta O(prefix-length), unas 32 comparaciones para IPv4, sin disco ni exploración.

A es falso: el planificador conoce los diccionarios. C es improbable con MaxMind (~3 MB para países). D es cierto, pero no explica la velocidad.


P10. Equivalencia de ILM — C

ClickHouse Cloud elimina la mayor parte de ILM:

  • Sin capas de nodos: objetos con caché local automática; no hay asignación hot/warm.
  • Sin rollover: una tabla MergeTree particionada sustituye índices rotativos.
  • Sin forcemerge programado: los merges son continuos.
  • TTL elimina: una cláusula sustituye la fase de 30 días.

Es uno de los mensajes más importantes en una conversación ES → CH: reduce la complejidad operativa un orden de magnitud.


P11. Por qué filtrar SpanKind = 'Server' — B

Los spans EventStream de long-poll de flagd duran ~10 minutos a propósito; son contabilidad de conexiones, no solicitudes de usuario, y dominan un «top 10 más lento» ingenuo.

En producción ocurre con RPC de larga duración (SignalR, streaming gRPC, suscripciones GraphQL, keep-alive MQTT). Filtra por SpanKind = 'Server' o un patrón SpanName al estudiar latencia visible por el usuario.

Modelo mental: spans Server = «lo que esperó el usuario»; Internal/Client/Producer/Consumer = «lo que ocurrió debajo».


P12. Indexación de texto completo — C

El índice text es el enfoque moderno, GA en ClickHouse 26.2, y es el que usa el esquema otel_logs_v2 del laboratorio en Body: El tipo text está pensado expresamente para este acceso.

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

Propiedades:

  • Tokenizer configurable: tokens, ngrams(N), sparseGrams o regex.
  • Más precisión que tokenbf_v1: postings reales por token, tasa de falsos positivos ~0 frente al ~0.025 predeterminado de tokenbf_v1.
  • Mayúsculas y CJK en tokenizadores nuevos; tokenbf_v1 no segmentaba Unicode.

B (tokenbf_v1) sigue siendo correcto antes de 26.2 y puede coexistir durante un rollover. A (minmax) sirve para columnas numéricas/fecha monotónicas. D (bloom_filter sin tokenizar) solo ayuda a igualdad de string completo, como WHERE Body = 'an exact full string', no subcadenas.

Modelo mental: para texto/subcadenas en String, text en 26.2+ y tokenbf_v1 en clústeres antiguos; para igualdad de un string de alta cardinalidad como TraceId, bloom_filter.


P13. Por qué ClickHouse no incluye un índice invertido como ES — B

Es un punto de diseño diferente, no una función ausente. Ambos permiten predicados de texto, optimizados para accesos distintos.

Índice invertido de Elasticsearch

Lucene lo usa como ruta primaria:

  • Granularidad: por documento; guarda token → posting list of (doc_id, term_frequency, positions).
  • Finalidad: encuentra documentos reales, no candidatos.
  • Ranking integrado: BM25 / TF-IDF.
  • Obligatorio por defecto: todo campo text paga el coste.
  • Almacenamiento: normalmente 30–50 % del documento.
  • Semántica rica: frases con slop, Levenshtein, boosting y DSL de scoring.

Índice text de ClickHouse (GA en 26.2)

Este índice text sigue siendo opcional.

Índice de salto sobre almacenamiento columnar:

  • Granularidad: por gránulo (8192 filas). Indica que «podría contener token X», no qué filas.
  • Finalidad: poda gránulos y después aplica LIKE fila a fila.
  • Sin ranking. Devuelve en orden de almacenamiento; sin BM25/TF-IDF.
  • Opcional: DDL explícita en una columna.
  • Almacenamiento: ~1–5 % de la columna, postings por gránulo.
  • Semántica: LIKE, ILIKE, hasToken, regex; sin fuzzy ni scoring.

Recorrido de WHERE Body LIKE '%timeout%' sobre 1.000 millones de filas

PasoElasticsearchClickHouse
1Tokeniza 'timeout' → [timeout]Igual
2Busca el token y obtiene 2,3 M de doc IDsRecorre el índice; quizá 5 K de 122 K gránulos contienen timeout
3Obtiene documentos, puntúa y ordenaLee columnas de 5 K gránulos (~41 M filas)
4(ninguno)Aplica LIKE '%timeout%' y obtiene ~2,3 M
Coste dominanteRecorrer postingsExplorar filas candidatas (~24× menos datos que sin índice)

Cuándo gana cada uno

CasoMejorMotivo
Búsqueda de productos ("running shoes $40–80")ESEl ranking es esencial
Autocompletado de documentaciónESFuzzy, frases y autocomplete
Logs («errores 5xx con timeout en una hora»)CHFiltrar y agregar miles de millones; ranking irrelevante
Seguridad («alerta por malicious_pattern»)CHExploración con poda
«100 logs más relevantes para texto libre»ESCH no puntúa
«Suma bytes_transferred donde path coincide /api/.* en 90 d»CHDomina la agregación

Resumen para el cliente

Ambos encuentran 'timeout'. La siguiente pregunta revela la diferencia:

  • Tras encontrar coincidencias, ES pregunta «¿cómo las ordeno por relevancia?».
  • ClickHouse pregunta «¿cuál es ahora la latencia p99 agrupada por servicio?».

Una disposición física no puede ser óptima para ambas: ES elige documentos + ranking; ClickHouse, columnas + analítica.


P14. Estrategia de evolución de esquema — B

Procede de la Decisión 3 del ADR de la Parte 2:

  • Map por defecto conserva flexibilidad; una clave nueva llega a LogAttributes sin DDL.
  • Promover claves usadas a columnas cuando ≥30 % de filas la tienen y ≥2 dashboards/alertas la usan.
  • Es no destructivo: ALTER TABLE ... ADD COLUMN MATERIALIZED LogAttributes['X'] permite coexistir a filas antiguas y nuevas.

A (JSON sin promoción) es flexible, pero sin vía rápida y ~2× más lento que Map en este volumen. C (una columna por atributo posible) falla con atributos nuevos. D no es migración.


P15. Contrapartida del muestreo — B

El muestreo por cola aplaza la decisión hasta recibir todos los spans y puede usar señales de la traza completa: ¿algún error? ¿latencia alta? tail_sampling_processor de OpenTelemetry es la implementación canónica; cuesta búfer y complejidad en el collector.

A (cabecera) decide al empezar, antes del error, incompatible con «conservar todas las trazas con error». C (por span) rompe la coherencia. D (sin muestreo) rara vez es viable con 1 millón de trazas/s.

En producción suele usarse cola con reglas: 100 % de errores, 5 % de lentas correctas y 0,1 % de rápidas correctas.


Guía de puntuación

PuntuaciónSignificado
14–15Has interiorizado ClickHouse y OTel; puedes dirigir una migración.
11–13Aprobado; revisa las explicaciones falladas. Esquema y modelo de datos predicen mejor el éxito con clientes.
8–10Al límite. Revisa las respuestas modelo de la Parte 2.
≤ 7Repite los puntos didácticos antes de los escenarios aplicados.

En esta página

ES