Skip to main content
La caché de consultas permite calcular las consultas SELECT una sola vez y servir las ejecuciones posteriores de la misma consulta directamente desde la caché. Según el tipo de consultas, esto puede reducir drásticamente la latencia y el consumo de recursos del servidor de ClickHouse.

Antecedentes, diseño y limitaciones

Las cachés de consultas, por lo general, pueden considerarse transaccionalmente coherentes o incoherentes.
  • En las cachés transaccionalmente coherentes, la base de datos invalida (descarta) los resultados de consultas almacenados en caché si el resultado de la consulta SELECT cambia o puede cambiar. En ClickHouse, las operaciones que modifican los datos incluyen inserciones/actualizaciones/eliminaciones en/de tablas o merges de colapso. El almacenamiento en caché transaccionalmente coherente es especialmente adecuado para bases de datos OLTP, por ejemplo MySQL (que eliminó la caché de consultas a partir de la versión 8.0) y Oracle.
  • En las cachés transaccionalmente incoherentes, se aceptan pequeñas imprecisiones en los resultados de las consultas bajo el supuesto de que a todas las entradas de caché se les asigna un período de validez tras el cual expiran (p. ej., 1 minuto) y de que los datos subyacentes cambian muy poco durante ese período. En general, este enfoque es más adecuado para bases de datos OLAP. Como ejemplo de un caso en el que el almacenamiento en caché transaccionalmente incoherente es suficiente, considere un informe horario de ventas en una herramienta de informes al que acceden simultáneamente varios usuarios. Por lo general, los datos de ventas cambian lo bastante despacio como para que la base de datos solo necesite calcular el informe una vez (representado por la primera consulta SELECT). Las consultas posteriores pueden servirse directamente desde la caché de consultas. En este ejemplo, un período de validez razonable podría ser de 30 min.
Tradicionalmente, el almacenamiento en caché transaccionalmente incoherente lo proporcionan herramientas cliente o paquetes proxy (p. ej., chproxy) que interactúan con la base de datos. Como resultado, la misma lógica de almacenamiento en caché y la misma configuración suelen duplicarse. Con la caché de consultas de ClickHouse, la lógica de almacenamiento en caché pasa al lado del servidor. Esto reduce el esfuerzo de mantenimiento y evita redundancias.

Ajustes de configuración y uso

En ClickHouse Cloud, debe usar ajustes a nivel de consulta para editar los ajustes de la caché de consultas. Actualmente no se admite la edición de ajustes a nivel de configuración.
clickhouse-local ejecuta una sola consulta a la vez. Como no tiene sentido almacenar en caché en memoria los resultados de las consultas para un único proceso, la caché de consultas en memoria está deshabilitada en clickhouse-local. La caché de consultas en disco sí está disponible: sus entradas residen en una caché del sistema de archivos en disco, por lo que los resultados almacenados en caché por un proceso de clickhouse-local pueden servirse a procesos posteriores que usen el mismo directorio de la caché del sistema de archivos.
El ajuste use_query_cache puede utilizarse para controlar si una consulta específica o todas las consultas de la sesión actual deben usar la caché de consultas. Por ejemplo, la primera ejecución de la consulta
almacenará el resultado de la consulta en la caché de consultas. Las ejecuciones posteriores de la misma consulta (también con el parámetro use_query_cache = true) leerán el resultado calculado de la caché y lo devolverán inmediatamente.
Establecer use_query_cache y todos los demás ajustes relacionados con la caché de consultas solo surte efecto en sentencias SELECT independientes. En particular, los resultados de SELECT sobre vistas creadas mediante CREATE VIEW AS SELECT [...] SETTINGS use_query_cache = true no se almacenan en la caché, a menos que la sentencia SELECT se ejecute con SETTINGS use_query_cache = true.
La forma en que se utiliza la caché puede configurarse con más detalle mediante los ajustes enable_writes_to_query_cache y enable_reads_from_query_cache (ambos con el valor predeterminado true). El primero controla si los resultados de las consultas se almacenan en la caché, mientras que el segundo determina si la base de datos debe intentar recuperar resultados de consultas desde la caché. Por ejemplo, la siguiente consulta usará la caché solo de forma pasiva; es decir, intentará leer de ella, pero no almacenar su resultado en ella:
Para tener el máximo control, por lo general se recomienda proporcionar los ajustes use_query_cache, enable_writes_to_query_cache y enable_reads_from_query_cache solo en consultas específicas. También es posible habilitar el almacenamiento en caché a nivel de usuario o de perfil (por ejemplo, mediante SET use_query_cache = true), pero debe tenerse en cuenta que, en ese caso, todas las consultas SELECT pueden devolver resultados almacenados en caché. La caché de consultas puede vaciarse mediante la sentencia SYSTEM CLEAR QUERY CACHE. El contenido de la caché de consultas se muestra en la tabla del sistema system.query_cache. El número de aciertos y fallos de la caché de consultas desde el inicio de la base de datos se muestra como eventos “QueryCacheHits” y “QueryCacheMisses” en la tabla del sistema system.events. Ambos contadores solo se actualizan para consultas SELECT que se ejecutan con el ajuste use_query_cache = true; las demás consultas no afectan a “QueryCacheMisses”. Ambos describen la caché de consultas en su conjunto: con la caché de consultas en disco habilitada (véase más abajo), una consulta que produce un fallo en memoria y luego se sirve desde el disco cuenta como un “QueryCacheHits”, y una consulta que produce un fallo en ambos backends cuenta como un “QueryCacheMisses”. El campo query_cache_usage en la tabla del sistema system.query_log muestra, para cada consulta ejecutada, si el resultado de la consulta se escribió en o se leyó de la caché de consultas. Las métricas QueryCacheEntries y QueryCacheBytes en la tabla del sistema system.metrics muestran cuántas entradas / bytes contiene actualmente la caché de consultas en memoria. No tienen en cuenta las entradas de la caché de consultas en disco, que actualmente no dispone de métricas de ocupación. La caché de consultas existe una vez por cada proceso del servidor ClickHouse. Sin embargo, de forma predeterminada, los resultados almacenados en caché no se comparten entre usuarios. Esto puede cambiarse (véase más abajo), pero no se recomienda hacerlo por motivos de seguridad. Los resultados de las consultas se identifican en la caché de consultas mediante el Abstract Syntax Tree (AST) de su consulta. Esto significa que el almacenamiento en caché no distingue entre mayúsculas y minúsculas; por ejemplo, SELECT 1 y select 1 se tratan como la misma consulta. Para que la coincidencia sea más natural, todos los ajustes de consulta relacionados con la caché de consultas y con el formato de salida) se eliminan del AST. Si la consulta se interrumpe debido a una excepción o a una cancelación por parte del usuario, no se escribe ninguna entrada en la caché de consultas. El tamaño de la caché de consultas en bytes, el número máximo de entradas de la caché y el tamaño máximo de cada entrada individual de la caché (en bytes y en registros) se pueden configurar mediante distintas opciones de configuración del servidor.
También es posible limitar el uso de la caché por usuario mediante perfiles de configuración y restricciones de configuración. Más concretamente, puede restringir la cantidad máxima de memoria (en bytes) que un usuario puede asignar a la caché de consultas en memoria y el número máximo de resultados de consultas almacenados (estos límites no se aplican a la caché de consultas en disco). Para ello, primero defina las configuraciones query_cache_max_size_in_bytes y query_cache_max_entries en un perfil de usuario de users.xml y, a continuación, establezca ambas configuraciones como solo lectura:
Para definir cuánto tiempo debe ejecutarse como mínimo una consulta para que su resultado pueda almacenarse en caché, puede usar la opción de configuración query_cache_min_query_duration. Por ejemplo, el resultado de la consulta
solo se almacena en caché si la consulta tarda más de 5 segundos en ejecutarse. También es posible especificar cuántas veces debe ejecutarse una consulta antes de que su resultado se almacene en caché; para ello, use la configuración query_cache_min_query_runs. Las entradas de la caché de consultas se vuelven obsoletas tras un determinado período de tiempo (time-to-live). De forma predeterminada, este período es de 60 segundos, pero se puede especificar un valor diferente a nivel de sesión, perfil o consulta mediante la configuración query_cache_ttl. La caché de consultas desaloja las entradas “de forma diferida”, es decir, cuando una entrada se vuelve obsoleta, no se elimina inmediatamente de la caché. En su lugar, cuando se va a insertar una nueva entrada en la caché de consultas, la base de datos comprueba si la caché tiene suficiente espacio libre para ella. Si no es así, la base de datos intenta eliminar todas las entradas obsoletas. Si la caché sigue sin tener suficiente espacio libre, la nueva entrada no se inserta. Si la consulta se ejecuta a través de HTTP, ClickHouse establece los encabezados Age y Expires con la antigüedad (en segundos) y la marca temporal de expiración de la entrada almacenada en caché. Las entradas de la caché de consultas se comprimen de forma predeterminada. Esto reduce el consumo total de memoria a costa de escrituras en / lecturas desde la caché de consultas más lentas. Para deshabilitar la compresión, use la configuración query_cache_compress_entries. A veces resulta útil mantener en caché varios resultados de la misma consulta. Esto se puede lograr mediante la configuración query_cache_tag, que actúa como una etiqueta (o espacio de nombres) para las entradas de la caché de consultas. La caché de consultas considera distintos los resultados de la misma consulta con diferentes etiquetas. Ejemplo para crear tres entradas diferentes en la caché de consultas para la misma consulta:
Para eliminar solo las entradas con la etiqueta tag de la caché de consultas, puede usar la sentencia SYSTEM CLEAR QUERY CACHE TAG 'tag'.

Almacenamiento en caché de subconsultas

De forma predeterminada, use_query_cache en la consulta externa no se aplica a las subconsultas. Esto significa que cada subconsulta debe habilitar explícitamente el almacenamiento en caché:
En este ejemplo, solo se almacena en caché el resultado de la subconsulta interna. La consulta externa no se almacena en caché. Para habilitar el almacenamiento en caché de todas las subconsultas de una sola vez, use la opción query_cache_for_subqueries:
Para desactivar explícitamente el almacenamiento en caché de una subconsulta específica mientras la propagación por lotes está habilitada, establezca use_query_cache = false en esa subconsulta:
Las entradas de caché de subconsultas de la caché de consultas en memoria son visibles en system.query_cache con is_subquery = 1. Los resultados de las subconsultas también se almacenan en caché en disco si la caché de consultas en disco está habilitada, pero estas entradas no se muestran en system.query_cache. La opción query_cache_ttl también se aplica a las entradas de caché de subconsultas y puede configurarse para cada subconsulta. ClickHouse lee los datos de las tablas en bloques de max_block_size filas. Debido al filtrado, la agregación, etc., los bloques de resultados suelen ser mucho más pequeños que ‘max_block_size’, aunque también hay casos en los que son mucho mayores. La opción query_cache_squash_partial_results (habilitada de forma predeterminada) controla si los bloques de resultados se compactan (si son muy pequeños) o se dividen (si son grandes) en bloques de tamaño ‘max_block_size’ antes de insertarlos en la caché de resultados de consultas. Esto reduce el rendimiento de las escrituras en la caché de consultas, pero mejora la tasa de compresión de las entradas de caché y proporciona una granularidad de bloque más natural cuando más adelante los resultados de las consultas se sirven desde la caché de consultas. Como resultado, la caché de consultas almacena varios bloques de resultados parciales para cada consulta. Aunque este comportamiento es una buena configuración predeterminada, puede desactivarse con la opción query_cache_squash_partial_results. Además, los resultados de consultas con funciones no deterministas no se almacenan en caché de forma predeterminada. Estas funciones incluyen: Para forzar de todos modos el almacenamiento en caché de los resultados de consultas con funciones no deterministas, use la opción query_cache_nondeterministic_function_handling. Los resultados de las consultas que involucran tablas del sistema (p. ej., system.processes` o information_schema.tables) no se almacenan en caché de forma predeterminada. Para forzar igualmente el almacenamiento en caché de los resultados de consultas con tablas del sistema, utilice la opción query_cache_system_table_handling. Por último, las entradas de la caché de consultas no se comparten entre usuarios por motivos de seguridad. Por ejemplo, el usuario A no debe poder eludir una ROW POLICY en una tabla ejecutando la misma consulta que otro usuario B para quien no existe esa política. Sin embargo, si es necesario, las entradas de caché pueden marcarse como accesibles para otros usuarios (es decir, compartidas) mediante la opción query_cache_share_between_users.

Caché de consultas en disco

De forma predeterminada, la caché de consultas mantiene sus entradas en memoria. Además, los resultados de las consultas pueden almacenarse en caché en disco, respaldados por una caché del sistema de archivos. En comparación con la caché de consultas en memoria, la caché de consultas en disco ofrece más espacio y persiste tras los reinicios del servidor. Una entrada solo puede servirla el mismo build del servidor que la escribió: tras una actualización o un downgrade, las entradas escritas por el build anterior pasan a ser fallos de caché y, con el tiempo, se sobrescriben o se desalojan. Para usar la caché de consultas en disco, primero configure una caché del sistema de archivos en la sección filesystem_caches de la configuración del servidor:
luego especifique su nombre en la configuración query_cache_on_disk_cache_name:
La caché de consultas en disco funciona de forma independiente de la caché de consultas en memoria y puede habilitarse por separado para lecturas y/o escrituras mediante las configuraciones enable_reads_from_query_cache_on_disk y enable_writes_to_query_cache_on_disk (ambas true de forma predeterminada). Si las lecturas están habilitadas para ambas cachés, la búsqueda se intenta primero en memoria y, solo si falla, en disco. Si las escrituras están habilitadas para ambas cachés, el resultado se almacena en las dos. Si ninguna caché puede almacenar el resultado (p. ej., en clickhouse-local, que no tiene caché de consultas en memoria, con enable_writes_to_query_cache_on_disk = false), la consulta no se rechaza por funciones no deterministas, tablas del sistema o un modo de desbordamiento distinto de throw: estas comprobaciones solo sirven para evitar que se almacenen resultados incorrectos. Las entradas en disco se comprimen con el códec especificado en la configuración query_cache_on_disk_codec (ZSTD(3) de forma predeterminada). Las configuraciones del servidor max_entry_size_in_bytes y max_entry_size_in_rows de la caché de consultas limitan las entradas de ambos backends; cada backend mide una entrada según la forma en que la almacena, por lo que en disco el límite de bytes se aplica a la entrada serializada y comprimida. Si se establece cualquiera de ellas en 0, se deshabilitan las escrituras en ambos backends. Todas las demás configuraciones de la caché de consultas (p. ej., query_cache_ttl, query_cache_min_query_duration) se aplican a la caché de consultas en disco igual que a la caché de consultas en memoria, con las dos excepciones que se indican a continuación. Las configuraciones query_cache_max_size_in_bytes y query_cache_max_entries son límites por usuario que solo afectan a la caché de consultas en memoria. No se aplican a la caché de consultas en disco: las entradas en disco están limitadas por el tamaño de la caché del sistema de archivos configurada y se desalojan según sus reglas (véase más abajo), por lo que un usuario con un perfil de configuración restrictivo puede almacenar en disco más resultados de los que estas configuraciones permiten en memoria. Esto es intencionado: la caché del sistema de archivos es un recurso compartido y de tamaño limitado en el que la caché de consultas en disco no reserva espacio. La configuración query_cache_share_between_users es otra excepción: tiene el mismo significado para ambos backends (una entrada escrita con ella se sirve a todos los usuarios), pero las entradas no compartidas se aíslan de forma diferente. La caché de consultas en memoria mantiene una única entrada por consulta y la rechaza al leerla si pertenece a otro usuario, por lo que una entrada no compartida del usuario A oculta las escrituras del usuario B hasta que la entrada caduca o es desalojada. En cambio, la caché de consultas en disco incluye el usuario y el conjunto de sus roles actuales en la clave de una entrada no compartida, de modo que el usuario B no encuentra la entrada del usuario A y almacena su propia copia del resultado. Lo mismo ocurre con un mismo usuario con distintos conjuntos de roles actuales, p. ej., después de SET ROLE: los roles conllevan privilegios y políticas de filas, por lo que los resultados calculados con roles distintos se almacenan en caché por separado. Las entradas de la caché de consultas en disco son entradas ordinarias de la caché del sistema de archivos, indistinguibles de otros datos almacenados en caché en la misma caché del sistema de archivos: no están protegidas contra la eliminación y se desalojan según las mismas reglas (p. ej., la menos usada recientemente) cuando la caché del sistema de archivos se queda sin espacio. No existen límites propios para su tamaño total ni para su número: solo se aplican los límites de la propia caché del sistema de archivos. La sentencia SYSTEM CLEAR QUERY CACHE (y SYSTEM CLEAR QUERY CACHE TAG 'tag') también elimina las entradas (con la etiqueta indicada; las entradas corruptas y las de un formato incompatible se eliminan independientemente de su etiqueta) de la caché de consultas en disco de la caché del sistema de archivos seleccionada mediante el ajuste query_cache_on_disk_cache_name de la sesión (p. ej., después de SET query_cache_on_disk_cache_name = 'query_results'), y deja intactos los demás datos de la caché del sistema de archivos. Para encontrar las entradas, recorre la metadata de todos los segmentos de archivo de la caché del sistema de archivos (en memoria, sin leer del disco) y lee el encabezado de cada clave marcada como entrada de la caché de consultas. El recorrido tarda un tiempo proporcional al número de segmentos de archivo de la caché del sistema de archivos, por lo que dedicar una caché del sistema de archivos exclusivamente a resultados de consultas hace que SYSTEM CLEAR QUERY CACHE resulte poco costosa. Si la caché del sistema de archivos ya ha desalojado el inicio de una entrada, el resto de la entrada se conserva hasta que la caché del sistema de archivos lo desaloje o se vuelva a escribir el mismo resultado de consulta, pero nunca se sirve. Una entrada que todavía se está escribiendo también se conserva. La sentencia SYSTEM DROP FILESYSTEM CACHE '<name>' las elimina junto con todo lo demás de la caché del sistema de archivos. La tabla del sistema system.query_cache y las métricas QueryCacheEntries y QueryCacheBytes muestran solo las entradas de la caché de consultas en memoria. El número de aciertos y fallos de la caché de consultas en disco se muestra en los eventos “QueryCacheOnDiskHits” y “QueryCacheOnDiskMisses” de la tabla del sistema system.events. Estos eventos desglosan únicamente el backend en disco, mientras que “QueryCacheHits” y “QueryCacheMisses” siguen siendo los contadores de la caché de consultas en su conjunto, y “QueryCacheAgeSeconds” contabiliza los aciertos de ambos backends. La caché en memoria se consulta primero, por lo que el número de aciertos en memoria es “QueryCacheHits” menos “QueryCacheOnDiskHits”.