Introdução
- Uma reordenação completa
- Um subconjunto da tabela original em uma ordem diferente
- Uma agregação pré-computada (semelhante a uma visão materializada), mas com uma ordenação alinhada à agregação.
Como funcionam as projeções?
- Usar corretamente os índices primários
- Pré-calcular agregações
Armazenamento mais inteligente com _part_offset
_part_offset em
projeções, o que traz uma nova forma de definir uma projeção.
Agora há duas formas de definir uma projeção:
- Armazenar colunas completas (o comportamento original): A projeção contém os dados completos e pode ser lida diretamente, oferecendo melhor desempenho quando os filtros correspondem à chave de ordenação da projeção.
-
Armazenar apenas a chave de ordenação +
_part_offset: A projeção funciona como um índice. O ClickHouse usa o índice primário da projeção para localizar as linhas correspondentes, mas lê os dados reais da tabela base. Isso reduz a sobrecarga de armazenamento, ao custo de um pouco mais de I/O no momento da consulta.
_part_offset.
Quando usar projeções?
- Projeções não permitem usar TTL diferente para a tabela de origem e a tabela de destino (oculta); visões materializadas permitem TTLs diferentes.
- Atualizações leves e exclusões não têm suporte em tabelas com projeções.
- Visões materializadas podem ser encadeadas: a tabela de destino de uma visão materializada pode ser a tabela de origem de outra visão materializada, e assim por diante. Isso não é possível com projeções.
- Definições de projeção não oferecem suporte a junções, mas visões materializadas oferecem. No entanto, consultas em tabelas com projeções podem usar junções livremente.
- Definições de projeção não oferecem suporte a filtros (cláusula
WHERE), mas visões materializadas oferecem. No entanto, consultas em tabelas com projeções podem filtrar livremente.
- É necessário um reordenamento completo dos dados. Embora a expressão na
projeção possa, em teoria, usar um
GROUP BY,visões materializadas são mais eficazes para manter agregações. O otimizador de consultas também tem maior probabilidade de aproveitar projeções que usam um reordenamento simples, isto é,SELECT * ORDER BY x. Você pode selecionar um subconjunto de colunas nessa expressão para reduzir o espaço de armazenamento. - Os usuários estiverem confortáveis com o possível aumento no uso de armazenamento e com a sobrecarga de gravar os dados duas vezes. Teste o impacto na velocidade de inserção e avalie a sobrecarga de armazenamento.
Exemplos
Filtragem por colunas que não estão na chave primária
pickup_datetime.
Vamos escrever uma consulta simples para encontrar todos os IDs de viagem em que os passageiros
deram ao motorista uma gorjeta superior a $200:
Observe que, como estamos filtrando por tip_amount, que não está no ORDER BY, o ClickHouse
precisou fazer uma varredura completa da tabela. Vamos acelerar essa consulta.
Para preservar a tabela original e os resultados, criaremos uma nova tabela e copiaremos os dados usando um INSERT INTO SELECT:
ALTER TABLE em conjunto com a
instrução ADD PROJECTION:
MATERIALIZE PROJECTION
para que os dados contidos nela sejam fisicamente ordenados e reescritos de acordo
com a consulta especificada acima:
system.query_log:
Usando projeções para acelerar consultas do UK Price Paid
town nem price estavam na cláusula ORDER BY quando
criamos a tabela:
INSERT INTO SELECT:
prj_oby_town_price, que produz uma
tabela adicional (oculta) com um índice primário, ordenada por cidade e preço, para
otimizar a consulta que lista os condados de uma cidade específica pelos maiores
preços pagos:
mutations_sync é
usada para forçar a execução síncrona.
Criamos e populamos a projeção prj_gby_county — uma tabela adicional (oculta)
que pré-calcula de forma incremental os valores agregados de avg(price) para todos os
130 condados existentes do Reino Unido:
Se houver uma cláusula
GROUP BY usada em uma projeção, como na projeção prj_gby_county
acima, o mecanismo de armazenamento subjacente da tabela (oculta)
passa a ser AggregatingMergeTree, e todas as funções de agregação são convertidas em
AggregateFunction. Isso garante a agregação incremental correta dos dados.uk_price_paid_with_projections
e de suas duas projeções:
Se agora executarmos novamente a consulta que lista os condados de Londres com os três
maiores preços pagos, veremos uma melhora no desempenho da consulta:
Da mesma forma, para a consulta que lista os condados do Reino Unido com os três
maiores preços médios pagos:
Observe que ambas as consultas visam a tabela original e que ambas resultaram
em uma varredura completa da tabela (todas as 30,03 milhões de linhas foram lidas do disco) antes de
criarmos as duas projeções.
Além disso, observe que a consulta que lista os condados de Londres para os três preços
mais altos está lendo 2,17 milhões de linhas. Quando usamos diretamente uma segunda tabela
otimizada para essa consulta, apenas 81,92 mil linhas foram lidas do disco.
O motivo da diferença é que, atualmente, a otimização optimize_read_in_order
mencionada acima não tem suporte a projeções.
Inspecionamos a tabela system.query_log para ver que o ClickHouse
usou automaticamente as duas projeções nas duas consultas acima (veja a
coluna de projeções abaixo):
Mais exemplos
CREATE AS e INSERT INTO SELECT.
Criar uma projeção
toYear(date), district e town:
optimize_use_projections, que é ativada por padrão.
Consulta 1. Preço médio por ano
Consulta 2. Preço médio por ano em Londres
Consulta 3. Os bairros mais caros
toYear(date) >= 2020):
Novamente, o resultado é o mesmo, mas observe a melhora no desempenho da consulta na 2ª consulta.
Combinando projeções em uma consulta
_part_offset introduzido na
versão anterior, o ClickHouse agora pode usar várias projeções para acelerar
uma única consulta com vários filtros.
É importante destacar que o ClickHouse ainda lê dados de apenas uma projeção (ou da tabela base),
mas pode usar os índices primários de outras projeções para eliminar partes desnecessárias antes da leitura.
Isso é especialmente útil para consultas que filtram por várias colunas, cada
uma potencialmente correspondente a uma projeção diferente.
Atualmente, esse mecanismo elimina apenas partes inteiras. A eliminação no nível de grânulo ainda não é compatível.Para demonstrar isso, definimos a tabela (com projeções usando colunas
_part_offset)
e inserimos cinco linhas de exemplo que correspondem aos diagramas acima.
Observação: A tabela usa configurações personalizadas para fins de ilustração, como grânulos com uma única linha
e mesclagens de partes desabilitadas, o que não é recomendado para uso em produção.
- Cinco partes separadas (uma por linha inserida)
- Uma entrada no índice primário por linha (na tabela base e em cada projeção)
- Cada parte contém exatamente uma linha
region e user_id.
Como o índice primário da tabela base é construído a partir de event_date e id, ele
não ajuda nesse caso; por isso, o ClickHouse usa:
region_projpara eliminar partes com base na regiãouser_id_projpara reduzir ainda mais as partes com base emuser_id
EXPLAIN projections = 1, que mostra como
o ClickHouse seleciona e aplica projeções.
EXPLAIN (mostrada acima) revela o plano lógico da consulta, de cima para baixo:
No fim, apenas 1 de 5 partes é lida da tabela base.
Ao combinar a análise de índices de várias projeções, o ClickHouse reduz significativamente a quantidade de dados examinados,
melhorando o desempenho e mantendo baixa a sobrecarga de armazenamento.