Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Índices primários

Como funciona o índice primário esparso no ClickHouse?


O índice primário esparso no ClickHouse ajuda a identificar com eficiência grânulos — blocos de linhas — que podem conter dados que correspondam à condição de uma consulta nas colunas de chave primária da tabela. Na próxima seção, explicamos como esse índice é construído com base nos valores dessas colunas.

Criação do índice primário esparso

Para ilustrar como o índice primário esparso é construído, usamos a tabela uk_price_paid_simple junto com algumas animações.

Como lembrete, na nossa tabela de exemplo ① com a chave primária (town, street), os dados ② inseridos são ③ armazenados em disco, ordenados pelos valores das colunas da chave primária e comprimidos, em arquivos separados para cada coluna:



Para processamento, os dados de cada coluna são ④ divididos logicamente em grânulos — cada um cobrindo 8.192 linhas —, que são as menores unidades com que o mecanismo de processamento de dados do ClickHouse trabalha.

Essa estrutura em grânulos também é o que torna o índice primário esparso: em vez de indexar cada linha, o ClickHouse armazena ⑤ os valores da chave primária de apenas uma linha por grânulo — especificamente, a primeira linha. Isso resulta em uma entrada de índice por grânulo:



Graças à sua esparsidade, o índice primário é pequeno o suficiente para caber inteiramente na memória, permitindo filtragem rápida para consultas com predicados nas colunas da chave primária. Na próxima seção, mostramos como ele ajuda a acelerar essas consultas.

Uso do índice primário

Mostramos como o índice primário esparso é usado para acelerar consultas com outra animação:



Analisar o predicado da consulta

A consulta de exemplo inclui um predicado em ambas as colunas da chave primária: town = 'LONDON' AND street = 'OXFORD STREET'.

Carregar o índice primário

Para acelerar a consulta, o ClickHouse carrega na memória o índice primário da tabela.

Identificar grânulos correspondentes

Em seguida, ele percorre as entradas do índice para identificar quais grânulos podem conter linhas que correspondam ao predicado — em outras palavras, quais grânulos não podem ser ignorados.

Carregar e processar grânulos

Esses grânulos potencialmente relevantes são então carregados e processados na memória, junto com os grânulos correspondentes de quaisquer outras colunas necessárias para a consulta.

Monitoramento de índices primários

Cada parte de dados da tabela tem seu próprio índice primário. Podemos inspecionar o conteúdo desses índices usando a função de tabela mergeTreeIndex.

A consulta a seguir lista o número de entradas no índice primário de cada parte de dados da nossa tabela de exemplo:

SELECT
    part_name,
    max(mark_number) AS entries
FROM mergeTreeIndex('uk', 'uk_price_paid_simple')
GROUP BY part_name;
   ┌─part_name─┬─entries─┐
1. │ all_2_2_0 │     914 │
2. │ all_1_1_0 │    1343 │
3. │ all_0_0_0 │    1349 │
   └───────────┴─────────┘

Esta consulta mostra as 10 primeiras entradas do índice primário de uma das partes de dados atuais. Observe que essas partes são continuamente mescladas em segundo plano, formando partes maiores:

SELECT 
    mark_number + 1 AS entry,
    town,
    street
FROM mergeTreeIndex('uk', 'uk_price_paid_simple')
WHERE part_name = (SELECT any(part_name) FROM mergeTreeIndex('uk', 'uk_price_paid_simple')) 
ORDER BY mark_number ASC
LIMIT 10;
    ┌─entry─┬─town───────────┬─street───────────┐
 1. │     1 │ ABBOTS LANGLEY │ ABBEY DRIVE      │
 2. │     2 │ ABERDARE       │ RICHARDS TERRACE │
 3. │     3 │ ABERGELE       │ PEN Y CAE        │
 4. │     4 │ ABINGDON       │ CHAMBRAI CLOSE   │
 5. │     5 │ ABINGDON       │ THORNLEY CLOSE   │
 6. │     6 │ ACCRINGTON     │ MAY HILL CLOSE   │
 7. │     7 │ ADDLESTONE     │ HARE HILL        │
 8. │     8 │ ALDEBURGH      │ LINDEN ROAD      │
 9. │     9 │ ALDERSHOT      │ HIGH STREET      │
10. │    10 │ ALFRETON       │ ALMA STREET      │
    └───────┴────────────────┴──────────────────┘

Por fim, usamos a cláusula EXPLAIN para ver como os índices primários de todas as partes de dados são usados para ignorar grânulos que não podem conter linhas que atendam aos predicados da consulta de exemplo. Esses grânulos são excluídos do carregamento e do processamento:

EXPLAIN indexes = 1
SELECT
    max(price)
FROM
    uk.uk_price_paid_simple
WHERE
    town = 'LONDON' AND street = 'OXFORD STREET';
    ┌─explain────────────────────────────────────────────────────────────────────────────────────────────────────┐
 1. │ Expression ((Project names + Projection))                                                                  │
 2. │   Aggregating                                                                                              │
 3. │     Expression (Before GROUP BY)                                                                           │
 4. │       Expression                                                                                           │
 5. │         ReadFromMergeTree (uk.uk_price_paid_simple)                                                        │
 6. │         Indexes:                                                                                           │
 7. │           PrimaryKey                                                                                       │
 8. │             Keys:                                                                                          │
 9. │               town                                                                                         │
10. │               street                                                                                       │
11. │             Condition: and((street in ['OXFORD STREET', 'OXFORD STREET']), (town in ['LONDON', 'LONDON'])) │
12. │             Parts: 3/3                                                                                     │
13. │             Granules: 3/3609                                                                               │
    └────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Observe como a linha 13 da saída do EXPLAIN acima mostra que apenas 3 de 3.609 grânulos, em todas as partes de dados, foram selecionados pela análise do índice primário para processamento. Os grânulos restantes foram ignorados por completo.

Também podemos observar que a maior parte dos dados foi ignorada simplesmente executando a consulta:

SELECT max(price)
FROM uk.uk_price_paid_simple
WHERE (town = 'LONDON') AND (street = 'OXFORD STREET');
   ┌─max(price)─┐
1. │  263100000 │ -- 263,10 milhões
   └────────────┘

1 row in set. Elapsed: 0.010 sec. Processed 24.58 thousand rows, 159.04 KB (2.53 million rows/s., 16.35 MB/s.)
Peak memory usage: 13.00 MiB.

Como mostrado acima, apenas cerca de 25.000 linhas foram processadas, de um total de aproximadamente 30 milhões de linhas na tabela de exemplo:

SELECT count() FROM uk.uk_price_paid_simple;
   ┌──count()─┐
1. │ 29556244 │ -- 29,56 milhões
   └──────────┘

Principais pontos

  • Índices primários esparsos ajudam o ClickHouse a ignorar dados desnecessários ao identificar quais grânulos podem conter linhas que correspondam às condições da consulta nas colunas da chave primária.

  • Cada índice armazena apenas os valores da chave primária da primeira linha de cada grânulo (um grânulo tem 8.192 linhas por padrão), tornando-se compacto o bastante para caber na memória.

  • Cada parte de dados em uma tabela MergeTree tem seu próprio índice primário, que é usado de forma independente durante a execução da consulta.

  • Durante as consultas, o índice permite que o ClickHouse ignore grânulos, reduzindo a E/S e o uso de memória e acelerando o desempenho.

  • Você pode inspecionar o conteúdo do índice usando a função de tabela mergeTreeIndex e monitorar o uso do índice com a cláusula EXPLAIN.

Onde encontrar mais informações

Para entender melhor como os índices primários esparsos funcionam no ClickHouse, incluindo como eles diferem dos índices tradicionais de banco de dados e as boas práticas de uso, confira nosso guia detalhado sobre indexação.

Se quiser entender como o ClickHouse processa, de forma altamente paralela, os dados selecionados pela varredura do índice primário, consulte o guia sobre paralelismo de consultas aqui.

Navigation