Cuándo usar diccionarios frente a JOINs
Los diccionarios funcionan mejor cuando uno de los lados de un JOIN es una tabla de referencia que cabe en memoria. Con un JOIN estándar, ClickHouse construye una tabla hash a partir del lado derecho antes de buscar en ella con el izquierdo, aunque la mayoría de las filas acaben descartándose después mediante filtrosWHERE. Aunque las versiones recientes (24.12+) aplican filtros antes de los JOINs en muchos casos, esto no siempre elimina la sobrecarga. Con un diccionario, llamas a dictGet directamente, por lo que las búsquedas solo se realizan sobre las filas que ya han superado el filtrado.
Sin embargo, dictGet no siempre es la opción adecuada. Si necesitas llamar a dictGet en un porcentaje elevado de las filas de una tabla —por ejemplo, en una condición WHERE como dictGet('dict', 'elevation', id) > 1800—, puede que te convenga más usar una columna normal con índices nativos. ClickHouse puede usar PREWHERE para omitir gránulos en una columna normal, pero dictGet se evalúa fila por fila sin soporte de índices.
Como regla general:
- Usa diccionarios para sustituir JOINs con tablas de dimensión pequeñas cuando la clave de búsqueda ya esté disponible.
- Usa columnas normales e índices cuando filtres por el valor buscado en muchas filas.
Elegir una disposición
La cláusulaLAYOUT controla la estructura de datos interna del diccionario. Todas las disposiciones disponibles están documentadas en la referencia de disposiciones.
Al elegir una disposición, use las siguientes pautas:
flat— la disposición más rápida (búsqueda simple por desplazamiento en un array), pero las claves deben serUInt64y, de forma predeterminada, están limitadas a 500.000 (max_array_size). Es la mejor opción para claves enteras que aumentan de forma monótona en tablas pequeñas o medianas. Las distribuciones de claves dispersas (por ejemplo, valores de clave 1 y 500.000) desperdician memoria, ya que el array se dimensiona según la clave más grande. Si está alcanzando el límite de 500k, es una señal de que debe cambiar ahashed_array.hashed_array— el valor predeterminado recomendado para la mayoría de los casos de uso. Almacena atributos en arrays con una tabla hash que asigna las claves a índices del array. Es casi tan rápido comohashed, pero usa la memoria de forma más eficiente, especialmente cuando hay muchos atributos.hashed— almacena el diccionario completo en una tabla hash. Puede ser más rápido quehashed_arraycuando tiene muy pocos atributos, pero consume más memoria a medida que aumenta la cantidad de atributos.complex_key_hashed/complex_key_hashed_array— úselos cuando las claves no puedan convertirse aUInt64(por ejemplo, clavesString). Siguen las mismas compensaciones de rendimiento que sus equivalentes no complejos.sparse_hashed— intercambia CPU por un menor uso de memoria en comparación conhashed. Rara vez es la mejor opción: solo es eficiente cuando tiene un único atributo. En la mayoría de los casos,hashed_arraysuele ser una mejor alternativa.cache/ssd_cache— solo almacenan en caché las claves a las que se accede con frecuencia. Son útiles cuando el conjunto de datos completo no cabe en memoria, pero las búsquedas pueden acudir al origen si se produce un fallo de caché. No se recomiendan para cargas de trabajo sensibles a la latencia ni para un enriquecimiento fiable en el momento de la inserción; consulte Diccionarios de caché en rutas de ingestión.direct— consulta el origen en cada búsqueda, sin almacenamiento en memoria. Úselo cuando los datos cambian con demasiada frecuencia como para almacenarlos en caché o cuando el diccionario es demasiado grande para la memoria.
Diccionarios de caché en rutas de ingestión
Las disposiciones basadas en caché (cache, complex_key_cache, ssd_cache y complex_key_ssd_cache) conservan las entradas precargadas únicamente en el servidor local. Cada servidor precarga su caché de forma independiente, y tras un reinicio o reemplazo el servidor arranca con la caché vacía. Las búsquedas con la caché en frío dependen de la fuente del diccionario, lo que añade latencia y puede bloquear o hacer fallar las inserciones si la fuente no logra resolver la búsqueda. En ClickHouse Cloud, el escalado automático y el reemplazo administrado de nodos hacen que esta situación sea más probable.
Evite estas disposiciones para el enriquecimiento en tiempo de inserción en vistas materializadas incrementales o en columnas MATERIALIZED y DEFAULT cuando sea importante contar con una latencia predecible y con la disponibilidad de la fuente. En su lugar, utilice una disposición que cargue el diccionario completo en cada servidor: normalmente hashed o hashed_array para claves UInt64, y complex_key_hashed o complex_key_hashed_array para claves de tipo cadena o compuestas. flat y otras disposiciones de carga completa también pueden ser adecuadas. Si el diccionario completo no cabe en memoria, enriquezca los datos antes de insertarlos o en el momento de la consulta, en lugar de depender de una caché precargada en la ruta de ingestión.
Monitoreo del uso de diccionarios
Supervise el consumo de memoria y el estado mediante la tablasystem.dictionaries:
bytes_allocated— memoria consumida por el diccionario. Los diccionarios almacenan los datos sin comprimir, por lo que este valor puede ser significativamente mayor que el tamaño comprimido de la tabla.hit_rateyfound_rate— útiles para evaluar la eficacia del diseñocache.last_exception— consúltalo cuando un diccionario no se cargue o no se actualice.