Skip to main content
O cache de consultas permite processar consultas SELECT apenas uma vez e atender às execuções subsequentes da mesma consulta diretamente do cache. Dependendo do tipo de consulta, isso pode reduzir drasticamente a latência e o consumo de recursos do servidor ClickHouse.

Contexto, design e limitações

Os caches de consultas geralmente podem ser classificados como transacionalmente consistentes ou inconsistentes.
  • Em caches transacionalmente consistentes, o banco de dados invalida (descarta) os resultados de consultas em cache se o resultado da consulta SELECT mudar ou puder mudar. No ClickHouse, as operações que alteram os dados incluem inserções/atualizações/exclusões em/de tabelas ou merges de colapsamento. O cache transacionalmente consistente é especialmente adequado para bancos de dados OLTP, como MySQL (que removeu o cache de consultas após a v8.0) e Oracle.
  • Em caches transacionalmente inconsistentes, pequenas imprecisões nos resultados das consultas são aceitas, partindo do pressuposto de que todas as entradas de cache recebem um período de validade após o qual expiram (por exemplo, 1 minuto) e de que os dados subjacentes mudam pouco durante esse período. Essa abordagem é, em geral, mais adequada para bancos de dados OLAP. Como exemplo de caso em que o cache transacionalmente inconsistente é suficiente, considere um relatório de vendas por hora em uma ferramenta de relatórios acessado simultaneamente por vários usuários. Em geral, os dados de vendas mudam devagar o suficiente para que o banco de dados precise computar o relatório apenas uma vez (representado pela primeira consulta SELECT). As consultas seguintes podem ser atendidas diretamente pelo cache de consultas. Neste exemplo, um período de validade razoável poderia ser de 30 min.
O cache transacionalmente inconsistente é tradicionalmente fornecido por ferramentas cliente ou pacotes de proxy (por exemplo, chproxy) que interagem com o banco de dados. Como resultado, a mesma lógica de cache e configuração costuma ser duplicada. Com o cache de consultas do ClickHouse, a lógica de cache passa para o lado do servidor. Isso reduz o esforço de manutenção e evita redundância.

Configurações e uso

No ClickHouse Cloud, você deve usar configurações em nível de consulta para editar as configurações do cache de consultas. No momento, não há suporte para editar configurações em nível de configuração.
O clickhouse-local executa uma única consulta por vez. Como armazenar em cache resultados de consultas na memória não faz sentido para um único processo, o cache de consultas em memória fica desativado no clickhouse-local. O cache de consultas em disco está disponível: suas entradas ficam em um cache do sistema de arquivos em disco, de modo que os resultados armazenados em cache por um processo do clickhouse-local podem ser reaproveitados por processos posteriores que usem o mesmo diretório do cache do sistema de arquivos.
A configuração use_query_cache pode ser usada para controlar se uma consulta específica ou todas as consultas da sessão atual devem usar o cache de consultas. Por exemplo, a primeira execução da consulta
armazenará o resultado da consulta no cache de consultas. Execuções subsequentes da mesma consulta (também com o parâmetro use_query_cache = true) irão ler do cache o resultado já calculado e retorná-lo imediatamente.
Definir use_query_cache e todas as outras configurações relacionadas ao cache de consultas só tem efeito em instruções SELECT independentes. Em particular, os resultados de SELECTs em views criadas por CREATE VIEW AS SELECT [...] SETTINGS use_query_cache = true não são armazenados em cache, a menos que a instrução SELECT seja executada com SETTINGS use_query_cache = true.
A forma como o cache é utilizado pode ser configurada em mais detalhes usando as configurações enable_writes_to_query_cache e enable_reads_from_query_cache (ambas true por padrão). A primeira configuração controla se os resultados das consultas são armazenados no cache, enquanto a segunda determina se o banco de dados deve tentar recuperar do cache os resultados das consultas. Por exemplo, a consulta a seguir usará o cache apenas de forma passiva, ou seja, tentará ler dele, mas não armazenará nele o seu resultado:
Para ter o máximo de controle, em geral recomenda-se fornecer as configurações use_query_cache, enable_writes_to_query_cache e enable_reads_from_query_cache apenas em consultas específicas. Também é possível habilitar o cache no nível do usuário ou do perfil (por exemplo, via SET use_query_cache = true), mas é preciso ter em mente que todas as consultas SELECT podem, nesse caso, retornar resultados em cache. O cache de consultas pode ser limpo usando a instrução SYSTEM CLEAR QUERY CACHE. O conteúdo do cache de consultas é exibido na tabela de sistema system.query_cache. O número de acertos e falhas do cache de consultas desde a inicialização do banco de dados é mostrado como eventos “QueryCacheHits” e “QueryCacheMisses” na tabela de sistema system.events. Ambos os contadores são atualizados apenas para consultas SELECT executadas com a configuração use_query_cache = true; outras consultas não afetam “QueryCacheMisses”. Eles descrevem o cache de consultas como um todo: com o cache de consultas em disco habilitado (veja abaixo), uma consulta que não é encontrada na memória e é então atendida a partir do disco conta como um “QueryCacheHits”, e uma consulta que não é encontrada em nenhum dos dois backends conta como um “QueryCacheMisses”. O campo query_cache_usage na tabela de sistema system.query_log mostra, para cada consulta executada, se o resultado da consulta foi gravado no cache de consultas ou lido dele. As métricas QueryCacheEntries e QueryCacheBytes na tabela de sistema system.metrics mostram quantas entradas / bytes o cache de consultas em memória contém no momento. Elas não contabilizam as entradas do cache de consultas em disco, que atualmente não possui métricas de ocupação. O cache de consultas existe uma vez por processo do servidor ClickHouse. No entanto, por padrão, os resultados em cache não são compartilhados entre usuários. Isso pode ser alterado (veja abaixo), mas não é recomendado por motivos de segurança. Os resultados de consulta são referenciados no cache de consultas pela Abstract Syntax Tree (AST) de sua consulta. Isso significa que o caching não diferencia maiúsculas de minúsculas; por exemplo, SELECT 1 e select 1 são tratadas como a mesma consulta. Para tornar a correspondência mais natural, todas as configurações em nível de consulta relacionadas ao cache de consultas e à formatação de saída) são removidas da AST. Se a consulta for abortada devido a uma exceção ou a um cancelamento pelo usuário, nenhuma entrada será gravada no cache de consultas. O tamanho do cache de consultas em bytes, o número máximo de entradas de cache e o tamanho máximo de entradas individuais de cache (em bytes e em registros) podem ser configurados por meio de diferentes opções de configuração do servidor.
Também é possível limitar o uso do cache por usuários individuais usando settings profiles e restrições de configurações. Mais especificamente, você pode restringir a quantidade máxima de memória (em bytes) que um usuário pode alocar no cache de consultas em memória e o número máximo de resultados de consulta armazenados (esses limites não se aplicam ao cache de consultas em disco). Para isso, primeiro defina as configurações query_cache_max_size_in_bytes e query_cache_max_entries em um perfil de usuário no users.xml e, em seguida, torne ambas as configurações somente leitura:
Para definir por quanto tempo, no mínimo, uma consulta deve ser executada para que seu resultado possa ser armazenado em cache, você pode usar a configuração query_cache_min_query_duration. Por exemplo, o resultado da consulta
é armazenado em cache apenas se a consulta levar mais de 5 segundos para ser executada. Também é possível especificar quantas vezes uma consulta precisa ser executada até que seu resultado seja armazenado em cache — para isso, use a configuração query_cache_min_query_runs. As entradas no cache de consultas ficam desatualizadas após um determinado período (time-to-live). Por padrão, esse período é de 60 segundos, mas um valor diferente pode ser especificado no nível da sessão, do perfil ou da consulta usando a configuração query_cache_ttl. O cache de consultas remove entradas de forma “preguiçosa”, ou seja, quando uma entrada fica desatualizada, ela não é removida imediatamente do cache. Em vez disso, quando uma nova entrada precisa ser inserida no cache de consultas, o banco de dados verifica se o cache tem espaço livre suficiente para a nova entrada. Se esse não for o caso, o banco de dados tenta remover todas as entradas desatualizadas. Se o cache ainda não tiver espaço livre suficiente, a nova entrada não será inserida. Se a consulta for executada via HTTP, o ClickHouse definirá os cabeçalhos Age e Expires com a idade (em segundos) e o timestamp de expiração da entrada em cache. As entradas no cache de consultas são comprimidas por padrão. Isso reduz o consumo geral de memória, ao custo de gravações mais lentas no cache e leituras mais lentas a partir do cache de consultas. Para desabilitar a compressão, use a configuração query_cache_compress_entries. Às vezes, é útil manter em cache vários resultados da mesma consulta. Isso pode ser obtido usando a configuração query_cache_tag, que atua como um rótulo (ou espaço de nomes) para entradas do cache de consultas. O cache de consultas considera como diferentes os resultados da mesma consulta com tags diferentes. Exemplo de como criar três entradas diferentes no cache de consultas para a mesma consulta:
Para remover apenas as entradas com a tag tag do cache de consultas, você pode usar a instrução SYSTEM CLEAR QUERY CACHE TAG 'tag'.

Armazenamento em cache de subconsultas

Por padrão, use_query_cache na consulta externa não é propagado para as subconsultas. Isso significa que cada subconsulta deve habilitar explicitamente o armazenamento em cache:
Neste exemplo, apenas o resultado da subconsulta interna é armazenado em cache. A consulta externa não é armazenada em cache. Para habilitar o armazenamento em cache de todas as subconsultas de uma só vez, use a configuração query_cache_for_subqueries:
Para desativar explicitamente o armazenamento em cache de uma subconsulta específica enquanto a propagação em massa estiver habilitada, defina use_query_cache = false nessa subconsulta:
As entradas de cache de subconsultas do cache de consultas em memória são visíveis em system.query_cache com is_subquery = 1. Os resultados de subconsultas também são armazenados em cache em disco se o cache de consultas em disco estiver habilitado, mas essas entradas não são exibidas em system.query_cache. A configuração query_cache_ttl também se aplica às entradas de cache de subconsultas e pode ser definida para cada subconsulta. O ClickHouse lê os dados da tabela em blocos de max_block_size linhas. Devido à filtragem, agregação, etc., os blocos de resultado normalmente são muito menores que ‘max_block_size’, mas também há casos em que são bem maiores. A configuração query_cache_squash_partial_results (habilitada por padrão) controla se os blocos de resultado são combinados (se forem muito pequenos) ou divididos (se forem grandes) em blocos do tamanho de ‘max_block_size’ antes de serem inseridos no cache de resultados de consultas. Isso reduz o desempenho das gravações no cache de consultas, mas melhora a taxa de compressão das entradas de cache e proporciona uma granularidade de bloco mais natural quando os resultados da consulta são posteriormente servidos a partir do cache de consultas. Como resultado, o cache de consultas armazena, para cada consulta, vários blocos de resultado (parciais). Embora esse comportamento seja um bom padrão, ele pode ser suprimido usando a configuração query_cache_squash_partial_results. Além disso, os resultados de consultas com funções não determinísticas não são armazenados em cache por padrão. Essas funções incluem Para forçar o armazenamento em cache dos resultados de consultas com funções não determinísticas mesmo assim, use a configuração query_cache_nondeterministic_function_handling. Resultados de consultas que envolvem tabelas de sistema (por exemplo, system.processes` ou information_schema.tables) não são armazenados em cache por padrão. Para forçar mesmo assim o armazenamento em cache de resultados de consultas com tabelas de sistema, use a configuração query_cache_system_table_handling. Por fim, as entradas no cache de consultas não são compartilhadas entre usuários por motivos de segurança. Por exemplo, o usuário A não deve conseguir contornar uma ROW POLICY em uma tabela executando a mesma consulta que outro usuário B para quem essa política não existe. No entanto, se necessário, as entradas de cache podem ser marcadas como acessíveis a outros usuários (isto é, compartilhadas) informando a configuração query_cache_share_between_users.

Cache de consultas em disco

Por padrão, o cache de consultas mantém suas entradas em memória. Além disso, os resultados de consultas também podem ser armazenados em cache em disco, com suporte de um cache do sistema de arquivos. Em comparação com o cache de consultas em memória, o cache de consultas em disco oferece mais espaço e persiste após reinicializações do servidor. Uma entrada só é servida pela mesma compilação do servidor que a gravou: após um upgrade ou downgrade, as entradas gravadas pela compilação anterior resultam em falhas de cache e acabam sendo sobrescritas ou removidas. Para usar o cache de consultas em disco, primeiro configure um cache do sistema de arquivos na seção filesystem_caches da configuração do servidor:
em seguida, especifique o nome dele na configuração query_cache_on_disk_cache_name:
O cache de consultas em disco funciona de forma independente do cache de consultas em memória e pode ser habilitado separadamente para leituras e/ou gravações usando as configurações enable_reads_from_query_cache_on_disk e enable_writes_to_query_cache_on_disk (ambas true por padrão). Se as leituras estiverem habilitadas para ambos os caches, a consulta é feita primeiro na memória e, somente em caso de falha de cache, no disco. Se as gravações estiverem habilitadas para ambos os caches, o resultado é armazenado em ambos. Se nenhum cache puder armazenar o resultado (por exemplo, no clickhouse-local, que não possui cache de consultas em memória, com enable_writes_to_query_cache_on_disk = false), a consulta não é rejeitada por causa de funções não determinísticas, tabelas de sistema ou de um modo de overflow diferente de throw: essas verificações servem apenas para evitar o armazenamento de resultados incorretos. As entradas em disco são comprimidas com o codec especificado pela configuração query_cache_on_disk_codec (ZSTD(3) por padrão). As configurações de servidor max_entry_size_in_bytes e max_entry_size_in_rows do cache de consultas limitam as entradas de ambos os backends; cada backend mede uma entrada da forma como a armazena, de modo que, em disco, o limite de bytes se aplica à entrada serializada e comprimida. Definir qualquer uma delas como 0 desabilita as gravações em ambos os backends. Todas as demais configurações do cache de consultas (por exemplo, query_cache_ttl, query_cache_min_query_duration) se aplicam ao cache de consultas em disco da mesma forma que ao cache de consultas em memória, com as duas exceções a seguir. As configurações query_cache_max_size_in_bytes e query_cache_max_entries são limites por usuário que se aplicam apenas ao cache de consultas em memória. Elas não são aplicadas ao cache de consultas em disco: as entradas em disco são limitadas pelo tamanho do cache do sistema de arquivos configurado e removidas de acordo com as regras dele (veja abaixo), de modo que um usuário com um settings profile restritivo ainda pode armazenar mais resultados em disco do que essas configurações permitem em memória. Isso é intencional — o cache do sistema de arquivos é um recurso compartilhado e de tamanho limitado, no qual o cache de consultas em disco não reserva espaço. A configuração query_cache_share_between_users é outra exceção: ela tem o mesmo significado para ambos os backends — uma entrada gravada com ela é servida a todos os usuários —, mas as entradas não compartilhadas são isoladas de forma diferente. O cache de consultas em memória mantém uma única entrada por consulta e a rejeita na leitura se ela pertencer a outro usuário, de modo que uma entrada não compartilhada do usuário A sobrepõe-se às gravações do usuário B até que a entrada expire ou seja removida. Já o cache de consultas em disco inclui o usuário e o conjunto de funções atuais dele na chave de uma entrada não compartilhada, de modo que o usuário B não encontra a entrada do usuário A e armazena sua própria cópia do resultado. O mesmo vale para um único usuário com diferentes conjuntos de funções atuais, por exemplo, após SET ROLE: as funções carregam privilégios e row policies, portanto os resultados calculados com funções diferentes são armazenados em cache separadamente. As entradas do cache de consultas em disco são entradas comuns do cache do sistema de arquivos, indistinguíveis de outros dados armazenados em cache no mesmo cache do sistema de arquivos: elas não são protegidas contra exclusão e são removidas pelas mesmas regras (por exemplo, least-recently-used) quando o cache do sistema de arquivos fica sem espaço. Não há limites específicos para o tamanho total ou a quantidade dessas entradas; valem apenas os limites do próprio cache do sistema de arquivos. A instrução SYSTEM CLEAR QUERY CACHE (e SYSTEM CLEAR QUERY CACHE TAG 'tag') também remove as entradas (com a tag informada; entradas corrompidas e entradas em formato incompatível são removidas independentemente da tag) do cache de consultas em disco do cache do sistema de arquivos selecionado pela configuração query_cache_on_disk_cache_name da sessão (por exemplo, após SET query_cache_on_disk_cache_name = 'query_results'), mantendo intactos os demais dados do cache do sistema de arquivos. Para encontrar as entradas, ela percorre os metadados de todos os segmentos de arquivo do cache do sistema de arquivos (em memória, sem ler do disco) e lê o cabeçalho de cada chave marcada como entrada do cache de consultas. Essa varredura leva um tempo proporcional ao número de segmentos de arquivo no cache do sistema de arquivos; por isso, um cache do sistema de arquivos dedicado a resultados de consultas mantém o custo de SYSTEM CLEAR QUERY CACHE baixo. Se o cache do sistema de arquivos já tiver removido o início de uma entrada, o restante dela permanece armazenado até que o cache do sistema de arquivos o remova ou que o mesmo resultado de consulta seja gravado novamente, mas ele nunca é retornado. Uma entrada que ainda está sendo gravada também é mantida. A instrução SYSTEM DROP FILESYSTEM CACHE '<name>' remove essas entradas junto com todo o restante do cache do sistema de arquivos. A tabela de sistema system.query_cache e as métricas QueryCacheEntries e QueryCacheBytes mostram apenas as entradas do cache de consultas em memória. O número de acertos e falhas do cache de consultas em disco é exibido nos eventos “QueryCacheOnDiskHits” e “QueryCacheOnDiskMisses” da tabela de sistema system.events. Esses eventos refletem apenas o backend em disco, enquanto “QueryCacheHits” e “QueryCacheMisses” continuam sendo os contadores do cache de consultas como um todo, e “QueryCacheAgeSeconds” contabiliza acertos de ambos os backends. O cache em memória é verificado primeiro, portanto o número de acertos em memória é “QueryCacheHits” menos “QueryCacheOnDiskHits”.