Entenda o desempenho das consultas
Considerações gerais
- Análise sintática e análise da consulta
- Otimização de consultas
- Execução do pipeline de consulta
- Processamento final
Conjunto de dados
Identifique as consultas lentas
Logs de consultas
system.query_log.
Para cada consulta executada, o ClickHouse registra estatísticas como o tempo de execução da consulta, o número de linhas lidas e o uso de recursos, como CPU, uso de memória ou acertos no cache do sistema de arquivos.
Por isso, o log de consultas é um bom ponto de partida para investigar consultas lentas. Você pode identificar facilmente as consultas que demoram mais para ser executadas e ver as informações de uso de recursos de cada uma.
Vamos encontrar as cinco consultas mais demoradas no nosso conjunto de dados de táxis de Nova York.
query_duration_ms indica quanto tempo essa consulta específica levou para ser executada. Ao observar os resultados dos logs de consultas, podemos ver que a primeira consulta está levando 2967ms para ser executada, o que pode ser melhorado.
Você também pode querer saber quais consultas estão sobrecarregando o sistema, examinando a consulta que consome mais memória ou CPU.
enable_filesystem_cache como 0 para melhorar a reprodutibilidade.
Vamos entender um pouco melhor o que essas consultas fazem.
- A Consulta 1 calcula a distribuição de distâncias em corridas com velocidade média acima de 30 milhas por hora.
- A Consulta 2 encontra o número e o custo médio das corridas por semana.
- A Consulta 3 calcula o tempo médio de cada viagem no conjunto de dados.
Instrução Explain
nyc_taxi.trips_small_inferred. Em seguida, a cláusula WHERE é aplicada para filtrar as linhas com base nos valores calculados. Os dados filtrados são preparados para agregação e os quantis são calculados. Por fim, o resultado é ordenado e exibido.
Aqui, podemos observar que nenhuma primary key é utilizada, o que faz sentido, pois não definimos nenhuma ao criar a tabela. Como resultado, o ClickHouse está realizando uma varredura completa da tabela para executar a consulta.
Explain Pipeline
EXPLAIN Pipeline mostra a estratégia de execução concreta para a consulta. Com ele, você pode ver como o ClickHouse realmente executou o plano de consulta genérico que analisamos anteriormente.
Metodologia
user, tables ou databases de system.query_logs para restringir a busca.
Depois de identificar as consultas que deseja otimizar, você pode começar a trabalhar nelas. Um erro comum que os desenvolvedores cometem nesta etapa é mudar várias coisas ao mesmo tempo, executar experimentos ad hoc e geralmente acabar com resultados mistos — e, mais importante, sem entender bem o que tornou a consulta mais rápida.
A otimização de consultas exige método. Não estou falando de benchmarking avançado, mas de ter um processo simples para entender como suas mudanças afetam o desempenho da consulta — isso já pode ajudar bastante.
Comece identificando suas consultas lentas nos logs de consultas e, em seguida, investigue possíveis melhorias de forma isolada. Ao testar a consulta, certifique-se de desabilitar o cache do sistema de arquivos.
O ClickHouse usa cache para acelerar o desempenho da consulta em diferentes etapas. Isso é bom para o desempenho, mas, durante a solução de problemas, pode ocultar possíveis gargalos de E/S ou um esquema de tabela inadequado. Por esse motivo, sugiro desativar o cache do sistema de arquivos durante os testes. Certifique-se de mantê-lo habilitado no ambiente de produção.Depois de identificar possíveis otimizações, recomenda-se implementá-las uma a uma para acompanhar melhor como elas afetam o desempenho. Abaixo está um diagrama que descreve a abordagem geral. Por fim, tenha cuidado com casos atípicos; é bastante comum que uma consulta rode devagar, seja porque um usuário executou uma consulta ad hoc custosa, seja porque o sistema estava sob pressão por algum outro motivo. Você pode agrupar pelo campo normalized_query_hash para identificar consultas custosas que estão sendo executadas regularmente. Essas provavelmente são as que vale a pena investigar.
Otimização básica
Nullable
mta_tax e payment_type. Os demais campos não deveriam usar uma coluna Nullable.
Baixa cardinalidade
ratecode_id, pickup_location_id, dropoff_location_id e vendor_id, são boas candidatas ao tipo de campo LowCardinality.
Otimize o tipo de dado
Aplique as otimizações
Observamos algumas melhorias tanto no tempo de consulta quanto no uso de memória. Graças à otimização do esquema de dados, reduzimos o volume total de dados armazenados, o que melhora o consumo de memória e reduz o tempo de processamento.
Vamos verificar o tamanho das tabelas para ver a diferença.
A importância das chaves primárias
Os grânulos no ClickHouse são as menores unidades de dados lidas durante a execução da consulta. Eles contêm até um número fixo de linhas, determinado por index_granularity, com um valor padrão de 8192 linhas. Os grânulos são armazenados de forma contígua e ordenados pela chave primária.Selecionar um bom conjunto de chaves primárias é importante para o desempenho, e na prática é comum armazenar os mesmos dados em tabelas diferentes e usar diferentes conjuntos de chaves primárias para acelerar um conjunto específico de consultas. Outras opções compatíveis com o ClickHouse, como Projection ou visão materializada, permitem usar um conjunto diferente de chaves primárias sobre os mesmos dados. A segunda parte desta série de blogs abordará isso com mais detalhes.
Escolha as chaves primárias
- Use campos que sirvam de filtro na maioria das consultas
- Escolha primeiro colunas com menor cardinalidade
- Considere um componente temporal na sua chave primária, já que filtrar por tempo em um conjunto de dados com
timestampé bastante comum.
passenger_count, pickup_datetime e dropoff_datetime.
A cardinalidade de passenger_count é baixa (24 valores únicos) e ele é usado em nossas consultas lentas. Também adicionamos campos de timestamp (pickup_datetime e dropoff_datetime), pois eles podem ser usados com frequência em filtros.
Crie uma nova tabela com as chaves primárias e faça a ingestão dos dados novamente.
| Consulta 1 | |||
|---|---|---|---|
| Execução 1 | Execução 2 | Execução 3 | |
| Elapsed | 1.699 sec | 1.353 sec | 0.765 sec |
| Linhas processadas | 329.04 milhões | 329.04 milhões | 329.04 milhões |
| Peak memory | 440.24 MiB | 337.12 MiB | 444.19 MiB |
| Consulta 2 | |||
|---|---|---|---|
| Execução 1 | Execução 2 | Execução 3 | |
| Elapsed | 1.419 sec | 1.171 sec | 0.248 sec |
| Linhas processadas | 329.04 milhões | 329.04 milhões | 41.46 milhões |
| Peak memory | 546.75 MiB | 531.09 MiB | 173.50 MiB |
| Consulta 3 | |||
|---|---|---|---|
| Execução 1 | Execução 2 | Execução 3 | |
| Elapsed | 1.414 sec | 1.188 sec | 0.431 sec |
| Linhas processadas | 329.04 million | 329.04 million | 276.99 million |
| Pico de memória | 451.53 MiB | 265.05 MiB | 197.38 MiB |