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
- Em caches transacionalmente consistentes, o banco de dados invalida (descarta) os resultados de consultas em cache se o resultado da consulta
SELECTmudar ou puder mudar. No ClickHouse, as operações que alteram os dados incluem inserções/atualizações/exclusões em/de tabelas oumergesde 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.
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.
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.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:
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.
users.xml e, em seguida, torne ambas as configurações
somente leitura:
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:
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:
query_cache_for_subqueries:
use_query_cache = false nessa subconsulta:
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
- funções para acessar dicionários:
dictGet()etc. - Funções Definidas pelo Usuário sem a tag
<deterministic>true</deterministic>na definição XML, - funções que retornam a data ou hora atual:
now(),today(),yesterday()etc., - funções que retornam valores aleatórios:
randomString(),fuzzBits()etc., - funções cujo resultado depende do tamanho e da ordem, ou dos fragmentos internos usados no processamento de consultas:
nowInBlock()etc.,rowNumberInBlock(),runningDifference(),blockSize()etc., - funções que dependem do ambiente:
currentUser(),queryID(),getMacro()etc.
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çãofilesystem_caches da configuração do servidor:
query_cache_on_disk_cache_name:
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”.