Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Por que minha chave primária não é usada? Como posso verificar?

Verificando sua chave primária

Os usuários podem se deparar com casos em que a consulta fica mais lenta do que o esperado, acreditando que estão ordenando ou filtrando por uma chave primária. Neste artigo, mostramos como confirmar se a chave está sendo usada, destacando os motivos mais comuns pelos quais isso não acontece.

Crie a tabela

Considere a tabela simples a seguir:

CREATE TABLE logs
(
    `code` LowCardinality(String),
    `timestamp` DateTime64(3)
)
ENGINE = MergeTree
ORDER BY (code, toUnixTimestamp(timestamp))

Observe que nossa chave de ordenação inclui toUnixTimestamp(timestamp) como o segundo elemento.

Popule esta tabela com 100 milhões de linhas:

INSERT INTO logs SELECT
 ['200', '404', '502', '403'][toInt32(randBinomial(4, 0.1)) + 1] AS code,
    now() + toIntervalMinute(number) AS timestamp
FROM numbers(100000000)

0 rows in set. Elapsed: 15.845 sec. Processed 100.00 million rows, 800.00 MB (6.31 million rows/s., 50.49 MB/s.)

SELECT count()
FROM logs

Filtragem básica

Se filtrarmos por código, veremos no resultado o número de linhas examinadas: 49.15 thousand. Observe que isso é um subconjunto do total de 100 milhões de linhas.

SELECT count() AS c
FROM logs
WHERE code = '200'

Além disso, podemos confirmar o uso do índice com a cláusula EXPLAIN indexes=1:

EXPLAIN indexes = 1
SELECT count() AS c
FROM logs
WHERE code = '200'

Observe como o número de grânulos examinados 8012 é uma fração do total 12209. A seção destacada abaixo confirma o uso do código da chave primária.

PrimaryKey
  Keys: 
   code 

Os grânulos são a unidade de processamento de dados no ClickHouse, e cada um normalmente contém 8192 linhas. Para mais detalhes sobre grânulos e como eles são filtrados, recomendamos a leitura deste guia.

Filtragem por múltiplas chaves

Suponha que a filtragem seja feita por code e timestamp:

SELECT count()
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')

Neste caso, ambas as chaves de ordenação são usadas para filtrar linhas, de modo que seja necessário ler apenas 87 grânulos.

Uso de chaves para ordenação

O ClickHouse também pode aproveitar chaves de ordenação para ordenar os dados com eficiência. Especificamente,

Quando a configuração optimize_read_in_order está habilitada (por padrão), o ClickHouse server usa o índice da tabela e lê os dados na ordem da chave ORDER BY. Isso permite evitar a leitura de todos os dados quando um LIMIT é especificado. Assim, consultas sobre grandes volumes de dados com LIMITs pequenos são processadas mais rapidamente. Veja aqui e aqui para mais detalhes.

No entanto, isso exige que as chaves usadas estejam alinhadas.

Por exemplo, considere esta consulta:

SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10

Podemos confirmar que a otimização não foi aplicada aqui com EXPLAIN pipeline:

EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY timestamp ASC
LIMIT 10

A linha MergeTreeSelect(pool: ReadPool, algorithm: Thread) aqui não indica o uso da otimização, mas sim uma leitura padrão. Isso ocorre porque a chave de ordenação da nossa tabela usa toUnixTimestamp(Timestamp) e NÃO timestamp. Corrigir essa incompatibilidade resolve o problema:

EXPLAIN PIPELINE
SELECT *
FROM logs
WHERE (code = '200') AND (timestamp >= '2025-01-01 00:00:00') AND (timestamp <= '2026-01-01 00:00:00')
ORDER BY toUnixTimestamp(timestamp) ASC
LIMIT 10

Navigation