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
- 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
SELECTcambia 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.
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.
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.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:
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.
users.xml y, a continuación, establezca ambas configuraciones como
solo lectura:
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:
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é:
query_cache_for_subqueries:
use_query_cache = false en esa subconsulta:
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:
- funciones para acceder a diccionarios:
dictGet(), etc. - funciones definidas por el usuario sin la etiqueta
<deterministic>true</deterministic>en su definición XML, - funciones que devuelven la fecha o la hora actuales:
now(),today(),yesterday(), etc., - funciones que devuelven valores aleatorios:
randomString(),fuzzBits(), etc., - funciones cuyo resultado depende del tamaño, el orden o los fragmentos internos usados para el procesamiento de consultas:
nowInBlock(), etc.,rowNumberInBlock(),runningDifference(),blockSize(), etc., - funciones que dependen del entorno:
currentUser(),queryID(),getMacro(), etc.
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ónfilesystem_caches de la configuración del servidor:
query_cache_on_disk_cache_name:
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”.