> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-fbfa8bee.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Documentação sobre Busca Vetorial Exata e Aproximada

# Busca Vetorial Exata e Aproximada

O problema de encontrar os N pontos mais próximos, em um espaço multidimensional (vetorial), para um determinado ponto é conhecido como [busca do vizinho mais próximo](https://en.wikipedia.org/wiki/Nearest_neighbor_search) ou, resumidamente, busca vetorial.
Existem duas abordagens gerais para resolver a busca vetorial:

* A busca vetorial exata calcula a distância entre o ponto fornecido e todos os pontos do espaço vetorial. Isso garante a melhor precisão possível, ou seja, os pontos retornados são, de fato, os vizinhos mais próximos. Como o espaço vetorial é percorrido exaustivamente, a busca vetorial exata pode ser lenta demais para uso em cenários reais.
* A busca vetorial aproximada se refere a um conjunto de técnicas (por exemplo, estruturas de dados especiais, como grafos e florestas aleatórias) que calculam resultados muito mais rapidamente do que a busca vetorial exata. A precisão do resultado normalmente é "boa o suficiente" para uso prático. Muitas técnicas aproximadas oferecem parâmetros para ajustar o equilíbrio entre a precisão do resultado e o tempo de busca.

Uma busca vetorial (exata ou aproximada) pode ser escrita em SQL da seguinte forma:

```sql theme={null}
WITH [...] AS reference_vector
SELECT [...]
FROM table
WHERE [...] -- uma cláusula WHERE é opcional
ORDER BY <DistanceFunction>(vectors, reference_vector)
LIMIT <N>
```

Os pontos no espaço vetorial são armazenados em uma coluna `vectors` do tipo array, por exemplo, [Array(Float64)](/pt-BR/reference/data-types/array), [Array(Float32)](/pt-BR/reference/data-types/array) ou [Array(BFloat16)](/pt-BR/reference/data-types/array).
O vetor de referência é um array constante e é definido como uma expressão de tabela comum.
`<DistanceFunction>` calcula a distância entre o ponto de referência e todos os pontos armazenados.
Qualquer uma das [funções de distância](/pt-BR/reference/functions/regular-functions/distance-functions) disponíveis pode ser usada para isso.
`<N>` especifica quantos vizinhos devem ser retornados.

<div id="exact-nearest-neighbor-search">
  ## Busca vetorial exata
</div>

Uma busca vetorial exata pode ser realizada usando a consulta SELECT acima, sem alterações.
O tempo de execução dessas consultas geralmente é proporcional ao número de vetores armazenados e à sua dimensão, ou seja, ao número de elementos do Array.
Além disso, como o ClickHouse faz uma varredura por força bruta de todos os vetores, o tempo de execução também depende do número de threads usadas pela consulta (consulte a configuração [max\_threads](/pt-BR/reference/settings/session-settings#max_threads)).

<div id="exact-nearest-neighbor-search-example">
  ### Exemplo
</div>

```sql theme={null}
CREATE TABLE tab(id Int32, vec Array(Float32)) ENGINE = MergeTree ORDER BY id;

INSERT INTO tab VALUES (0, [1.0, 0.0]), (1, [1.1, 0.0]), (2, [1.2, 0.0]), (3, [1.3, 0.0]), (4, [1.4, 0.0]), (5, [1.5, 0.0]), (6, [0.0, 2.0]), (7, [0.0, 2.1]), (8, [0.0, 2.2]), (9, [0.0, 2.3]), (10, [0.0, 2.4]), (11, [0.0, 2.5]);

WITH [0., 2.] AS reference_vec
SELECT id, vec
FROM tab
ORDER BY L2Distance(vec, reference_vec) ASC
LIMIT 3;
```

retorna

```result theme={null}
   ┌─id─┬─vec─────┐
1. │  6 │ [0,2]   │
2. │  7 │ [0,2.1] │
3. │  8 │ [0,2.2] │
   └────┴─────────┘
```

<div id="approximate-nearest-neighbor-search">
  ## Busca vetorial aproximada
</div>

<div id="vector-similarity-index">
  ### Índices de similaridade vetorial
</div>

O ClickHouse fornece um índice especial de "similaridade vetorial" para realizar busca vetorial aproximada.

<Note>
  Os índices de similaridade vetorial estão disponíveis no ClickHouse versão 25.8 ou superior.
  Se você encontrar problemas, abra uma issue no [repositório do ClickHouse](https://github.com/clickhouse/clickhouse/issues).
</Note>

<div id="creating-a-vector-similarity-index">
  #### Como criar um índice de similaridade vetorial
</div>

Um índice de similaridade vetorial pode ser criado em uma nova tabela da seguinte forma:

```sql theme={null}
CREATE TABLE table
(
  [...],
  vectors Array(Float*),
  INDEX <index_name> vectors TYPE vector_similarity(<type>, <distance_function>, <dimensions>) [GRANULARITY <N>]
)
ENGINE = MergeTree
ORDER BY [...]
```

Alternativamente, para adicionar um índice de similaridade vetorial a uma tabela existente:

```sql theme={null}
ALTER TABLE table ADD INDEX <index_name> vectors TYPE vector_similarity(<type>, <distance_function>, <dimensions>) [GRANULARITY <N>];
```

Índices de similaridade vetorial são tipos especiais de índices de skipping (veja [aqui](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-data_skipping-indexes) e [aqui](/pt-BR/concepts/features/performance/skip-indexes/skipping-indexes)).
Assim, a instrução `ALTER TABLE` acima faz com que o índice seja criado apenas para dados inseridos futuramente na tabela.
Para criar o índice também para os dados existentes, você precisa materializá-lo:

```sql theme={null}
ALTER TABLE table MATERIALIZE INDEX <index_name> SETTINGS mutations_sync = 2;
```

A função `<distance_function>` deve ser

* `L2Distance`, a [distância euclidiana](https://en.wikipedia.org/wiki/Euclidean_distance), que representa o comprimento do segmento de reta entre dois pontos no espaço euclidiano,
* `cosineDistance`, a [distância de cosseno](https://en.wikipedia.org/wiki/Cosine_similarity#Cosine_distance), que representa o ângulo entre dois vetores não nulos, ou
* `dotProduct`, o [produto escalar](https://en.wikipedia.org/wiki/Dot_product) (produto interno), que representa a soma dos produtos elemento a elemento de dois vetores. Equivalente a `cosineDistance` em dados normalizados.

Para dados normalizados, `L2Distance` geralmente é a melhor escolha; caso contrário, recomenda-se `cosineDistance` para compensar a escala.

<Note>
  Para as funções de distância `L2Distance` e `cosineDistance`, um valor menor significa maior similaridade, enquanto para `dotProduct`, um valor maior significa maior similaridade.
  Como resultado, índices vetoriais com `L2Distance` e `cosineDistance` só podem ser usados por consultas `SELECT [...] ORDER BY [...] ASC` (`ASC` é o padrão de `ORDER BY`), enquanto índices vetoriais criados para `dotProduct` só podem ser usados por consultas `SELECT [...] ORDER BY [...] DESC`.
</Note>

`<dimensions>` especifica a cardinalidade do array (número de elementos) na coluna subjacente.
Se o ClickHouse encontrar um array com cardinalidade diferente durante a criação do índice, o índice será descartado e um erro será retornado.

O parâmetro opcional GRANULARITY `<N>` refere-se ao tamanho dos grânulos de índice (veja [aqui](/pt-BR/concepts/features/performance/skip-indexes/skipping-indexes)).
Ao contrário dos skip indexes comuns, que usam uma granularidade de índice padrão de 1, os índices de similaridade vetorial usam 100 milhões como granularidade de índice padrão.
Esse valor garante que apenas poucos índices sejam criados internamente, mesmo para partes grandes.
Recomendamos alterar a granularidade do índice apenas para usuários avançados que entendam as implicações do que estão fazendo (veja [abaixo](#differences-to-regular-skipping-indexes)).

Os índices de similaridade vetorial são genéricos no sentido de que podem acomodar diferentes métodos de busca aproximada.
O método efetivamente usado é especificado pelo parâmetro `<type>`.
No momento, o único método disponível é HNSW ([artigo acadêmico](https://arxiv.org/abs/1603.09320)), uma técnica popular e de última geração para busca vetorial aproximada baseada em grafos hierárquicos de proximidade.
Se HNSW for usado como tipo, os usuários poderão especificar opcionalmente outros parâmetros específicos do HNSW:

```sql theme={null}
CREATE TABLE table
(
  [...],
  vectors Array(Float*),
  INDEX index_name vectors TYPE vector_similarity('hnsw', <distance_function>, <dimensions>[, <quantization>, <hnsw_max_connections_per_layer>, <hnsw_candidate_list_size_for_construction>]) [GRANULARITY N]
)
ENGINE = MergeTree
ORDER BY [...]
```

Os seguintes parâmetros específicos do HNSW estão disponíveis:

* `<quantization>` controla a quantização dos vetores no grafo de proximidade. Os valores possíveis são `f64`, `f32`, `f16`, `bf16`, `i8` ou `b1`. O valor padrão é `bf16`. Observe que esse parâmetro não afeta a representação dos vetores na coluna subjacente.
* `<hnsw_max_connections_per_layer>` controla o número de vizinhos por nó do grafo, também conhecido como o hiperparâmetro HNSW `M`. O valor padrão é `32`. O valor `0` significa usar o valor padrão.
* `<hnsw_candidate_list_size_for_construction>` controla o tamanho da lista dinâmica de candidatos durante a construção do grafo HNSW, também conhecido como o hiperparâmetro HNSW `ef_construction`. O valor padrão é `128`. O valor `0` significa usar o valor padrão.

Os valores padrão de todos os parâmetros específicos do HNSW funcionam razoavelmente bem na maioria dos casos de uso.
Portanto, não recomendamos personalizar os parâmetros específicos do HNSW.

Aplicam-se as seguintes restrições adicionais:

* Índices de similaridade vetorial só podem ser criados em colunas do tipo [Array(Float32)](/pt-BR/reference/data-types/array), [Array(Float64)](/pt-BR/reference/data-types/array) ou [Array(BFloat16)](/pt-BR/reference/data-types/array). Arrays de tipos de ponto flutuante anuláveis e de baixa cardinalidade, como `Array(Nullable(Float32))` e `Array(LowCardinality(Float32))`, não são permitidos.
* Índices de similaridade vetorial devem ser criados em uma única coluna.
* Índices de similaridade vetorial podem ser criados em expressões calculadas (por exemplo, `INDEX index_name arraySort(vectors) TYPE vector_similarity([...])`), mas esses índices não podem ser usados posteriormente para busca aproximada de vizinhos.
* Índices de similaridade vetorial exigem que todos os arrays na coluna subjacente tenham `<dimension>` elementos — isso é verificado durante a criação do índice. Para detectar violações desse requisito o mais cedo possível, os usuários podem adicionar uma [restrição](/pt-BR/reference/statements/create/table#constraints) à coluna vetorial, por exemplo, `CONSTRAINT same_length CHECK length(vectors) = 256`.
* Da mesma forma, os valores de array na coluna subjacente não podem estar vazios (`[]`) nem ter o valor padrão (também `[]`).

**Estimando o consumo de armazenamento e memória**

Um vetor gerado para uso com um modelo de IA típico (por exemplo, um Large Language Model, [LLMs](https://en.wikipedia.org/wiki/Large_language_model)) consiste em centenas ou milhares de valores de ponto flutuante.
Assim, um único vetor pode consumir vários kilobytes de memória.
Os usuários que quiserem estimar o armazenamento necessário para a coluna vetorial subjacente na tabela, bem como a memória principal necessária para o índice de similaridade vetorial, podem usar as duas fórmulas abaixo:

Consumo de armazenamento da coluna vetorial na tabela (não comprimido):

```text theme={null}
Storage consumption = Number of vectors * Dimension * Size of column data type
```

Exemplo com o [conjunto de dados dbpedia](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M):

```text theme={null}
Storage consumption = 1 milhão * 1536 * 4 (para Float32) = 6,1 GB
```

O índice de similaridade vetorial deve ser totalmente carregado do disco na memória principal para realizar as buscas.
Da mesma forma, o índice vetorial também é construído integralmente na memória e depois salvo em disco.

Consumo de memória necessário para carregar um índice vetorial:

```text theme={null}
Memória para vetores no índice (mv) = Número de vetores * Dimensão * Tamanho do tipo de dado quantizado
Memória para o grafo em memória (mg) = Número de vetores * hnsw_max_connections_per_layer * Bytes_per_node_id (= 4) * Layer_node_repetition_factor (= 2)

Consumo de memória: mv + mg
```

Exemplo com o [conjunto de dados dbpedia](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M):

```text theme={null}
Memória para vetores no índice (mv) = 1 milhão * 1536 * 2 (para BFloat16) = 3072 MB
Memória para o grafo em memória (mg) = 1 milhão * 64 * 2 * 4 = 512 MB

Consumo de memória = 3072 + 512 = 3584 MB
```

A fórmula acima não considera a memória adicional exigida pelos índices de similaridade vetorial para alocar estruturas de dados em tempo de execução, como buffers pré-alocados e caches.

<div id="using-a-vector-similarity-index">
  #### Usando um índice de similaridade vetorial
</div>

<Note>
  Para usar índices de similaridade vetorial, a configuração [compatibility](/pt-BR/reference/settings/session-settings) deve estar definida como `''` (o valor padrão), ou `'25.1'` ou posterior.
</Note>

Os índices de similaridade vetorial suportam consultas SELECT neste formato:

```sql theme={null}
WITH [...] AS reference_vector
SELECT [...]
FROM table
WHERE [...] -- uma cláusula WHERE é opcional
ORDER BY <DistanceFunction>(vectors, reference_vector)
LIMIT <N>
```

O otimizador de consultas do ClickHouse tenta corresponder ao template de consulta acima e utilizar os índices de similaridade vetorial disponíveis.
Uma consulta só pode usar um índice de similaridade vetorial se a função de distância na consulta SELECT for a mesma que a função de distância na definição do índice.

Usuários avançados podem fornecer um valor personalizado para a configuração [hnsw\_candidate\_list\_size\_for\_search](/pt-BR/reference/settings/session-settings#hnsw_candidate_list_size_for_search) (também conhecida como hiperparâmetro HNSW "ef\_search") para ajustar o tamanho da lista de candidatos durante a busca (por exemplo, `SELECT [...] SETTINGS hnsw_candidate_list_size_for_search = <value>`).
O valor padrão da configuração, 256, funciona bem na maioria dos casos de uso.
Valores mais altos resultam em maior precisão, porém com desempenho mais lento.

Se a consulta puder usar um índice de similaridade vetorial, o ClickHouse verifica se o LIMIT `<N>` fornecido nas consultas SELECT está dentro de limites razoáveis.
Mais especificamente, um erro é retornado se `<N>` for maior que o valor da configuração [max\_limit\_for\_vector\_search\_queries](/pt-BR/reference/settings/session-settings#max_limit_for_vector_search_queries), cujo valor padrão é 100.
Valores de LIMIT muito grandes podem tornar as buscas mais lentas e geralmente indicam um erro de uso.

Para verificar se uma consulta SELECT utiliza um índice de similaridade vetorial, você pode adicionar o prefixo `EXPLAIN indexes = 1` à consulta.

Como exemplo, consulte

```sql theme={null}
EXPLAIN indexes = 1
WITH [0.462, 0.084, ..., -0.110] AS reference_vec
SELECT id, vec
FROM tab
ORDER BY L2Distance(vec, reference_vec) ASC
LIMIT 10;
```

pode retornar

```result theme={null}
┌─explain─────────────────────────────────────────────────────────────────────────────────────────┐
 1. │ Expression (Project names)                                                                      │
 2. │   Limit (preliminary LIMIT (without OFFSET))                                                    │
 3. │     Sorting (Sorting for ORDER BY)                                                              │
 4. │       Expression ((Before ORDER BY + (Projection + Change column names to column identifiers))) │
 5. │         ReadFromMergeTree (default.tab)                                                         │
 6. │         Indexes:                                                                                │
 7. │           PrimaryKey                                                                            │
 8. │             Condition: true                                                                     │
 9. │             Parts: 1/1                                                                          │
10. │             Granules: 575/575                                                                   │
11. │           Skip                                                                                  │
12. │             Name: idx                                                                           │
13. │             Description: vector_similarity GRANULARITY 100000000                                │
14. │             Parts: 1/1                                                                          │
15. │             Granules: 10/575                                                                    │
    └─────────────────────────────────────────────────────────────────────────────────────────────────┘
```

Neste exemplo, 1 milhão de vetores do [dataset dbpedia](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M), cada um com dimensão 1536, estão armazenados em 575 grânulos, ou seja, 1,7 mil linhas por grânulo.
A consulta solicita 10 vizinhos e o índice de similaridade vetorial localiza esses 10 vizinhos em 10 grânulos distintos.
Esses 10 grânulos serão lidos durante a execução da consulta.

Os índices de similaridade vetorial são utilizados quando a saída contém `Skip` e o nome e tipo do índice vetorial (no exemplo, `idx` e `vector_similarity`).
Nesse caso, o índice de similaridade vetorial descartou dois dos quatro grânulos, ou seja, 50% dos dados.
Quanto mais grânulos puderem ser descartados, mais eficaz será o uso do índice.

<Tip>
  Para forçar o uso do índice, você pode executar a consulta SELECT com a configuração [force\_data\_skipping\_indexes](/pt-BR/reference/settings/session-settings#force_data_skipping_indices) (informe o nome do índice como valor da configuração).
</Tip>

**Pós-filtragem e Pré-filtragem**

Os usuários podem, opcionalmente, especificar uma cláusula `WHERE` com condições de filtro adicionais para a consulta SELECT.
O ClickHouse avaliará essas condições de filtro usando a estratégia de pós-filtragem ou de pré-filtragem.
Em resumo, ambas as estratégias determinam a ordem em que os filtros são avaliados:

* Pós-filtragem significa que o índice de similaridade vetorial é avaliado primeiro; depois, ClickHouse avalia o(s) filtro(s) adicional(is) especificado(s) na cláusula `WHERE`.
* Pré-filtragem significa que a ordem de avaliação do filtro é inversa.

As estratégias apresentam diferentes trade-offs:

* A pós-filtragem tem o problema geral de poder retornar menos linhas do que o número solicitado na cláusula `LIMIT <N>`. Essa situação ocorre quando uma ou mais linhas de resultado retornadas pelo índice de similaridade vetorial não atendem aos filtros adicionais.
* A pré-filtragem geralmente ainda é um problema sem solução. Alguns bancos de dados vetoriais especializados oferecem algoritmos de pré-filtragem, mas a maioria dos bancos de dados relacionais (incluindo o ClickHouse) recorre à busca exata de vizinhos, isto é, a uma varredura por força bruta sem índice.

A estratégia usada depende da condição de filtro.

*Filtros adicionais fazem parte da chave de partição*

Se a condição de filtro adicional fizer parte da chave de partição, o ClickHouse aplicará a eliminação de partições.
Por exemplo, uma tabela é particionada por intervalo pela coluna `year` e a seguinte consulta é executada:

```sql theme={null}
WITH [0., 2.] AS reference_vec
SELECT id, vec
FROM tab
WHERE year = 2025
ORDER BY L2Distance(vec, reference_vec) ASC
LIMIT 3;
```

O ClickHouse descartará todas as partições, exceto a de 2025.

*Filtros adicionais não podem ser avaliados usando índices*

Se condições de filtro adicionais não puderem ser avaliadas usando índices (índice de chave primária, índice de skipping), o ClickHouse aplicará pós-filtragem.

*Filtros adicionais podem ser avaliados usando o índice de chave primária*

Se condições de filtro adicionais puderem ser avaliadas usando a [chave primária](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree#primary-key) (ou seja, elas formam um prefixo da chave primária) e

* se a condição de filtro eliminar pelo menos uma linha dentro de uma parte, o ClickHouse recorrerá à pré-filtragem para os intervalos "remanescentes" dentro da parte,
* se a condição de filtro não eliminar nenhuma linha dentro de uma parte, o ClickHouse aplicará pós-filtragem à parte.

Em casos de uso práticos, este último caso é bastante improvável.

*Filtros adicionais podem ser avaliados usando índice de skipping*

Se condições de filtro adicionais puderem ser avaliadas usando [índices de skipping](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-data_skipping-indexes) (índice minmax, índice set etc.), o ClickHouse aplicará pós-filtragem.
Nesses casos, o índice de similaridade vetorial é avaliado primeiro, pois espera-se que ele elimine mais linhas do que os outros índices de skipping.

Para um controle mais preciso sobre pós-filtragem vs. pré-filtragem, duas configurações podem ser usadas:

A configuração [vector\_search\_filter\_strategy](/pt-BR/reference/settings/session-settings#vector_search_filter_strategy) (padrão: `auto`, que implementa as heurísticas acima) pode ser definida como `prefilter`.
Isso é útil para forçar a pré-filtragem nos casos em que as condições de filtro adicionais são altamente seletivas.
Como exemplo, a consulta a seguir pode se beneficiar da pré-filtragem:

```sql theme={null}
SELECT bookid, author, title
FROM books
WHERE price < 2.00
ORDER BY cosineDistance(book_vector, getEmbedding('Books on ancient Asian empires'))
LIMIT 10
```

Supondo que apenas um número muito pequeno de livros custe menos de 2 dólares, a pós-filtragem pode retornar zero linhas, porque os 10 resultados mais próximos retornados pelo índice vetorial podem ter preço acima de 2 dólares.
Ao forçar a pré-filtragem (adicione `SETTINGS vector_search_filter_strategy = 'prefilter'` à consulta), o ClickHouse primeiro encontra todos os livros com preço inferior a 2 dólares e, em seguida, executa uma busca vetorial por força bruta nesses livros.

Como abordagem alternativa para resolver o problema acima, [vector\_search\_index\_fetch\_multiplier](/pt-BR/reference/settings/session-settings#vector_search_index_fetch_multiplier) (padrão: `1.0`, máximo: `1000.0`) pode ser configurado com um valor > `1.0` (por exemplo, `2.0`).
O número de vizinhos mais próximos buscados no índice vetorial é multiplicado pelo valor dessa configuração, e então o filtro adicional é aplicado a essas linhas para retornar a quantidade de linhas definida por LIMIT.
Como exemplo, podemos executar a consulta novamente, mas com o multiplicador `3.0`:

```sql theme={null}
SELECT bookid, author, title
FROM books
WHERE price < 2.00
ORDER BY cosineDistance(book_vector, getEmbedding('Books on ancient Asian empires'))
LIMIT 10
SETTING vector_search_index_fetch_multiplier = 3.0;
```

O ClickHouse buscará 3,0 x 10 = 30 vizinhos mais próximos do índice vetorial em cada parte e, em seguida, avaliará os filtros adicionais.
Apenas os dez vizinhos mais próximos serão retornados.
Observamos que definir `vector_search_index_fetch_multiplier` pode mitigar o problema, mas, em casos extremos (condição WHERE muito seletiva), ainda é possível que sejam retornadas menos de N linhas solicitadas.

**Rescoring**

Os skip indexes no ClickHouse geralmente filtram no nível de grânulo, ou seja, uma busca em um skip index (internamente) retorna uma lista de grânulos com possível correspondência, o que reduz a quantidade de dados lidos na varredura subsequente.
Isso funciona bem para skip indexes em geral, mas, no caso dos índices de similaridade vetorial, cria um "descompasso de granularidade".
Em mais detalhes, o índice de similaridade vetorial determina os números das linhas dos N vetores mais semelhantes para um determinado vetor de referência, mas depois precisa extrapolar esses números de linha para números de grânulos.
Em seguida, o ClickHouse carrega esses grânulos do disco e repete o cálculo de distância para todos os vetores nesses grânulos.
Essa etapa é chamada de rescoring e, embora teoricamente possa melhorar a precisão — lembre-se de que o índice de similaridade vetorial retorna apenas um resultado *aproximado* —, ela claramente não é ideal em termos de desempenho.

Por isso, o ClickHouse fornece uma otimização que desativa o rescoring e retorna diretamente do índice os vetores mais semelhantes e suas distâncias.
Essa otimização vem habilitada por padrão; consulte a configuração [vector\_search\_with\_rescoring](/pt-BR/reference/settings/session-settings#vector_search_with_rescoring).
Em alto nível, ela funciona assim: o ClickHouse disponibiliza os vetores mais semelhantes e suas distâncias como uma coluna virtual `_distances`.
Para ver isso, execute uma consulta de busca vetorial com `EXPLAIN header = 1`:

```sql theme={null}
EXPLAIN header = 1
WITH [0., 2.] AS reference_vec
SELECT id
FROM tab
ORDER BY L2Distance(vec, reference_vec) ASC
LIMIT 3
SETTINGS vector_search_with_rescoring = 0
```

```result theme={null}
Query id: a2a9d0c8-a525-45c1-96ca-c5a11fa66f47

    ┌─explain─────────────────────────────────────────────────────────────────────────────────────────────────┐
 1. │ Expression (Project names)                                                                              │
 2. │ Header: id Int32                                                                                        │
 3. │   Limit (preliminary LIMIT (without OFFSET))                                                            │
 4. │   Header: L2Distance(__table1.vec, _CAST([0., 2.]_Array(Float64), 'Array(Float64)'_String)) Float64     │
 5. │           __table1.id Int32                                                                             │
 6. │     Sorting (Sorting for ORDER BY)                                                                      │
 7. │     Header: L2Distance(__table1.vec, _CAST([0., 2.]_Array(Float64), 'Array(Float64)'_String)) Float64   │
 8. │             __table1.id Int32                                                                           │
 9. │       Expression ((Before ORDER BY + (Projection + Change column names to column identifiers)))         │
10. │       Header: L2Distance(__table1.vec, _CAST([0., 2.]_Array(Float64), 'Array(Float64)'_String)) Float64 │
11. │               __table1.id Int32                                                                         │
12. │         ReadFromMergeTree (default.tab)                                                                 │
13. │         Header: id Int32                                                                                │
14. │                 _distance Float32                                                                       │
    └─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
```

<Note>
  Uma consulta executada sem rescoring (`vector_search_with_rescoring = 0`) e com réplicas paralelas ativadas pode voltar a usar rescoring.
</Note>

<div id="performance-tuning">
  #### Ajuste de desempenho
</div>

**Ajuste da compressão**

Em praticamente todos os casos de uso, os vetores na coluna subjacente são densos e não se comprimem bem.
Como resultado, a [compressão](/pt-BR/reference/statements/create/table#column_compression_codec) deixa mais lentas as inserções e leituras na/da coluna de vetores.
Por isso, recomendamos desativar a compressão.
Para fazer isso, especifique `CODEC(NONE)` para a coluna de vetores assim:

```sql theme={null}
CREATE TABLE tab(id Int32, vec Array(Float32) CODEC(NONE), INDEX idx vec TYPE vector_similarity('hnsw', 'L2Distance', 2)) ENGINE = MergeTree ORDER BY id;
```

**Ajustando a criação de índices**

O ciclo de vida dos índices de similaridade vetorial está ligado ao ciclo de vida das partes.
Em outras palavras, sempre que uma nova parte com um índice de similaridade vetorial definido é criada, o índice também é criado.
Isso normalmente acontece quando os dados são [inseridos](/pt-BR/concepts/features/operations/insert/inserting-data) ou durante [mesclagens](/pt-BR/concepts/core-concepts/merges).
Infelizmente, o HNSW é conhecido pelos longos tempos de criação de índices, o que pode tornar inserções e mesclagens significativamente mais lentas.
Idealmente, os índices de similaridade vetorial devem ser usados apenas quando os dados são imutáveis ou raramente mudam.

Para acelerar a criação de índices, as seguintes técnicas podem ser usadas:

Primeiro, a criação de índices pode ser paralelizada.
O número máximo de threads para criação de índices pode ser configurado usando a configuração do servidor [max\_build\_vector\_similarity\_index\_thread\_pool\_size](/pt-BR/reference/settings/server-settings/settings#max_build_vector_similarity_index_thread_pool_size).
Para um desempenho ideal, esse valor deve ser configurado de acordo com o número de núcleos de CPU.

Segundo, para acelerar instruções INSERT, os usuários podem desativar a criação de índices de skipping em partes recém-inseridas usando a configuração de sessão [materialize\_skip\_indexes\_on\_insert](/pt-BR/reference/settings/session-settings#materialize_skip_indexes_on_insert).
Consultas SELECT nessas partes recorrerão à busca exata.
Como as partes inseridas tendem a ser pequenas em comparação com o tamanho total da tabela, espera-se que o impacto disso no desempenho seja insignificante.

Terceiro, para acelerar mesclagens, os usuários podem desativar a criação de índices de skipping em partes mescladas usando a configuração de sessão [materialize\_skip\_indexes\_on\_merge](/pt-BR/reference/settings/merge-tree-settings#materialize_skip_indexes_on_merge).
Isso, em conjunto com a instrução [ALTER TABLE \[...\] MATERIALIZE INDEX \[...\]](/pt-BR/reference/statements/alter/skipping-index#materialize-index), fornece controle explícito sobre o ciclo de vida dos índices de similaridade vetorial.
Por exemplo, a criação de índices pode ser adiada até que todos os dados tenham sido ingeridos ou até um período de baixa carga do sistema, como no fim de semana.

**Ajustando o uso de índices**

Consultas SELECT precisam carregar índices de similaridade vetorial na memória principal para usá-los.
Para evitar que o mesmo índice de similaridade vetorial seja carregado repetidamente na memória principal, o ClickHouse fornece um cache dedicado em memória para esses índices.
Quanto maior esse cache, menos carregamentos desnecessários ocorrerão.
O tamanho máximo do cache pode ser configurado usando a configuração do servidor [vector\_similarity\_index\_cache\_size](/pt-BR/reference/settings/server-settings/settings#vector_similarity_index_cache_size).
Por padrão, o cache pode crescer até 5 GB.

As seguintes mensagens de log (`system.text_log`) indicam que o índice de similaridade vetorial está sendo carregado.
Se essas mensagens aparecerem repetidamente em diferentes consultas de busca vetorial, isso indica que o tamanho do cache está baixo demais.

```text theme={null}
2026-02-03 07:39:10.351635 [1386] f0ac5c85-1b1c-4f35-8848-87a1d1aa00ba : VectorSimilarityIndex Start loading vector similarity index

<...>

2026-02-03 07:40:25.217603 [1386] f0ac5c85-1b1c-4f35-8848-87a1d1aa00ba : VectorSimilarityIndex Loaded vector similarity index: max_level = 2, connectivity = 64, size = 1808111, capacity = 1808111, memory_usage = 8.00 GiB, bytes_per_vector = 4096, scalar_words = 1024, nodes = 1808111, edges = 51356964, max_edges = 233395072
```

<Note>
  O cache do índice de similaridade vetorial armazena grânulos do índice vetorial.
  Se os grânulos individuais do índice vetorial forem maiores que o tamanho do cache, eles não serão armazenados em cache.
  Portanto, calcule o tamanho do índice vetorial (com base na fórmula em "Estimando o consumo de armazenamento e memória" ou em [system.data\_skipping\_indices](/pt-BR/reference/system-tables/data_skipping_indices)) e dimensione o cache de acordo.
</Note>

*Reiteramos que verificar e, se necessário, aumentar o cache do índice vetorial deve ser a primeira etapa ao investigar consultas lentas de busca vetorial.*

O tamanho atual do cache do índice de similaridade vetorial é exibido em [system.metrics](/pt-BR/reference/system-tables/metrics):

```sql theme={null}
SELECT metric, value
FROM system.metrics
WHERE metric = 'VectorSimilarityIndexCacheBytes'
```

Os acertos e as falhas de cache de uma consulta com um determinado ID de consulta podem ser obtidos em [system.query\_log](/pt-BR/reference/system-tables/query_log):

```sql theme={null}
SYSTEM FLUSH LOGS query_log;

SELECT ProfileEvents['VectorSimilarityIndexCacheHits'], ProfileEvents['VectorSimilarityIndexCacheMisses']
FROM system.query_log
WHERE type = 'QueryFinish' AND query_id = '<...>'
ORDER BY event_time_microseconds;
```

Para casos de uso em produção, recomendamos dimensionar o cache de modo que todos os índices vetoriais permaneçam na memória o tempo todo.

**Ajustando a quantização**

[A quantização](https://huggingface.co/blog/embedding-quantization) é uma técnica para reduzir o consumo de memória dos vetores e os custos computacionais de criar e percorrer índices vetoriais.
Os índices vetoriais do ClickHouse oferecem suporte às seguintes opções de quantização:

| Quantização   | Nome                        | Armazenamento por dimensão |
| ------------- | --------------------------- | -------------------------- |
| f32           | Precisão simples            | 4 bytes                    |
| f16           | Meia precisão               | 2 bytes                    |
| bf16 (padrão) | Meia precisão (brain float) | 2 bytes                    |
| i8            | Quarto de precisão          | 1 byte                     |
| b1            | Binário                     | 1 bit                      |

A quantização reduz a precisão das buscas vetoriais em comparação com a busca nos valores originais de ponto flutuante em precisão total (`f32`).
No entanto, na maioria dos datasets, a quantização brain float de meia precisão (`bf16`) resulta em perda de precisão desprezível; por isso, os índices de similaridade vetorial usam essa técnica de quantização por padrão.
A quantização em quarto de precisão (`i8`) e a quantização binária (`b1`) causam perda de precisão perceptível em buscas vetoriais.
Recomendamos essas duas quantizações apenas se o tamanho do índice de similaridade vetorial for significativamente maior que a DRAM disponível.
Nesse caso, também sugerimos habilitar o rescoring ([vector\_search\_index\_fetch\_multiplier](/pt-BR/reference/settings/session-settings#vector_search_index_fetch_multiplier), [vector\_search\_with\_rescoring](/pt-BR/reference/settings/session-settings#vector_search_with_rescoring)) para melhorar a precisão.
A quantização binária é recomendada apenas para 1) embeddings normalizados (ou seja, comprimento do vetor = 1; os modelos da OpenAI geralmente são normalizados) e 2) quando a distância de cosseno é usada como função de distância.
Internamente, a quantização binária usa a distância de Hamming para construir e pesquisar o grafo de proximidade.
A etapa de rescoring usa os vetores originais em precisão total armazenados na tabela para identificar os vizinhos mais próximos por meio da distância de cosseno.

**Ajustando a transferência de dados**

O vetor de referência em uma consulta de busca vetorial é fornecido pelo usuário e, em geral, obtido por meio de uma chamada a um Large Language Model (LLM).
Um código Python típico que executa uma busca vetorial no ClickHouse pode ser assim

```python theme={null}
search_v = openai_client.embeddings.create(input = "[Good Books]", model='text-embedding-3-large', dimensions=1536).data[0].embedding

params = {'search_v': search_v}
result = chclient.query(
   "SELECT id FROM items
    ORDER BY cosineDistance(vector, %(search_v)s)
    LIMIT 10",
    parameters = params)
```

Vetores de embedding (`search_v` no trecho acima) podem ter uma dimensionalidade muito alta.
Por exemplo, a OpenAI fornece modelos que geram vetores de embedding com 1536 ou até 3072 dimensões.
No código acima, o driver Python do ClickHouse substitui o vetor de embedding por uma string legível e, em seguida, envia a consulta SELECT inteiramente como uma string.
Supondo que o vetor de embedding seja composto por 1536 valores de ponto flutuante de precisão simples, a string enviada chega a 20 kB de comprimento.
Isso gera alto consumo de CPU para tokenização, parsing e milhares de conversões de string para float.
Além disso, é necessário um espaço considerável no arquivo de log do servidor ClickHouse, o que também causa crescimento excessivo em `system.query_log`.

Observe que a maioria dos modelos de LLM retorna um vetor de embedding como uma lista ou um array do NumPy de floats nativos.
Portanto, recomendamos que aplicações Python vinculem o parâmetro do vetor de referência em formato binário usando o seguinte estilo:

```python theme={null}
search_v = openai_client.embeddings.create(input = "[Good Books]", model='text-embedding-3-large', dimensions=1536).data[0].embedding

params = {'$search_v_binary$': np.array(search_v, dtype=np.float32).tobytes()}
result = chclient.query(
   "SELECT id FROM items
    ORDER BY cosineDistance(vector, reinterpret($search_v_binary$, 'Array(Float32)'))
    LIMIT 10"
    parameters = params)
```

No exemplo, o vetor de referência é enviado como está, em formato binário, e reinterpretado como um array de números de ponto flutuante no servidor.
Isso economiza tempo de CPU no servidor e evita o aumento desnecessário dos logs do servidor e de `system.query_log`.

<div id="administration">
  #### Administração e monitoramento
</div>

O tamanho em disco dos índices de similaridade vetorial pode ser consultado em [system.data\_skipping\_indices](/pt-BR/reference/system-tables/data_skipping_indices):

```sql theme={null}
SELECT database, table, name, formatReadableSize(data_compressed_bytes)
FROM system.data_skipping_indices
WHERE type = 'vector_similarity';
```

Exemplo de saída:

```result theme={null}
┌─database─┬─table─┬─name─┬─formatReadab⋯ssed_bytes)─┐
│ default  │ tab   │ idx  │ 348.00 MB                │
└──────────┴───────┴──────┴──────────────────────────┘
```

<div id="differences-to-regular-skipping-indexes">
  #### Diferenças em relação aos índices de skipping regulares
</div>

Assim como os [índices de skipping](/pt-BR/concepts/features/performance/skip-indexes/skipping-indexes) regulares, os índices de similaridade vetorial são construídos sobre grânulos, e cada bloco indexado consiste em `GRANULARITY = [N]` grânulos (`[N]` = 1 por padrão para índices de skipping normais).
Por exemplo, se a granularidade do índice primário da tabela for 8192 (configuração `index_granularity = 8192`) e `GRANULARITY = 2`, então cada bloco indexado conterá 16384 linhas.
No entanto, as estruturas de dados e os algoritmos para busca aproximada de vizinhos são inerentemente orientados a linhas.
Eles armazenam uma representação compacta de um conjunto de linhas e também retornam linhas para consultas de busca vetorial.
Isso gera algumas diferenças um tanto contraintuitivas na forma como os índices de similaridade vetorial se comportam em comparação com os índices de skipping normais.

Quando um usuário define um índice de similaridade vetorial em uma coluna, o ClickHouse cria internamente um "subíndice" de similaridade vetorial para cada bloco de índice.
O subíndice é "local", no sentido de que conhece apenas as linhas do bloco de índice ao qual pertence.
No exemplo anterior, supondo que uma coluna tenha 65536 linhas, obtemos quatro blocos de índice (abrangendo oito grânulos) e um subíndice de similaridade vetorial para cada bloco de índice.
Em teoria, um subíndice consegue retornar diretamente as linhas com os N pontos mais próximos dentro do seu bloco de índice.
No entanto, como o ClickHouse carrega dados do disco para a memória na granularidade de grânulos, os subíndices extrapolam as linhas correspondentes para a granularidade dos grânulos.
Isso difere dos índices de skipping regulares, que ignoram dados na granularidade dos blocos de índice.

O parâmetro `GRANULARITY` determina quantos subíndices de similaridade vetorial são criados.
Valores maiores de `GRANULARITY` significam menos subíndices de similaridade vetorial, porém maiores, até o ponto em que uma coluna (ou o data part de uma coluna) tenha apenas um único subíndice.
Nesse caso, o subíndice tem uma visão "global" de todas as linhas da coluna e pode retornar diretamente todos os grânulos da coluna (part) com linhas relevantes (há no máximo `LIMIT [N]` desses grânulos).
Em uma segunda etapa, o ClickHouse carregará esses grânulos e identificará as linhas realmente melhores realizando um cálculo de distância por força bruta sobre todas as linhas dos grânulos.
Com um valor pequeno de `GRANULARITY`, cada subíndice retorna até `LIMIT N` grânulos.
Como resultado, mais grânulos precisam ser carregados e pós-filtrados.
Observe que a precisão da busca é igualmente boa nos dois casos; apenas o desempenho do processamento difere.
Em geral, recomenda-se usar um `GRANULARITY` alto para índices de similaridade vetorial e recorrer a valores menores de `GRANULARITY` apenas em caso de problemas, como consumo excessivo de memória pelas estruturas de similaridade vetorial.
Se nenhum `GRANULARITY` tiver sido especificado para índices de similaridade vetorial, o valor padrão será 100 milhões.

<div id="approximate-nearest-neighbor-search-example">
  #### Exemplo
</div>

Consultas:

```sql title="Query" theme={null}
CREATE TABLE tab(id Int32, vec Array(Float32), INDEX idx vec TYPE vector_similarity('hnsw', 'L2Distance', 2)) ENGINE = MergeTree ORDER BY id;

INSERT INTO tab VALUES (0, [1.0, 0.0]), (1, [1.1, 0.0]), (2, [1.2, 0.0]), (3, [1.3, 0.0]), (4, [1.4, 0.0]), (5, [1.5, 0.0]), (6, [0.0, 2.0]), (7, [0.0, 2.1]), (8, [0.0, 2.2]), (9, [0.0, 2.3]), (10, [0.0, 2.4]), (11, [0.0, 2.5]);

WITH [0., 2.] AS reference_vec
SELECT id, vec
FROM tab
ORDER BY L2Distance(vec, reference_vec) ASC
LIMIT 3;
```

```result title="Response" theme={null}
   ┌─id─┬─vec─────┐
1. │  6 │ [0,2]   │
2. │  7 │ [0,2.1] │
3. │  8 │ [0,2.2] │
   └────┴─────────┘
```

Mais conjuntos de dados de exemplo que usam busca vetorial aproximada:

* [LAION-400M](/pt-BR/get-started/sample-datasets/laion)
* [LAION-5B](/pt-BR/get-started/sample-datasets/laion5b)
* [dbpedia](/pt-BR/get-started/sample-datasets/dbpedia)
* [hackernews](/pt-BR/get-started/sample-datasets/hacker-news-vector-search)

<div id="approximate-nearest-neighbor-search-qbit">
  ### Quantized Bit (QBit)
</div>

Uma abordagem comum para acelerar a busca vetorial exata é usar um [tipo de dado float](/pt-BR/reference/data-types/float) com menor precisão.
Por exemplo, se os vetores forem armazenados como `Array(BFloat16)` em vez de `Array(Float32)`, o tamanho dos dados será reduzido pela metade, e espera-se que o tempo de execução das consultas diminua proporcionalmente.
Esse método é conhecido como quantização. Embora acelere o processamento, ele pode reduzir a precisão dos resultados, apesar de realizar uma varredura exaustiva de todos os vetores.

Com a quantização tradicional, perdemos precisão tanto durante a busca quanto no armazenamento dos dados. No exemplo acima, armazenaríamos `BFloat16` em vez de `Float32`, o que significa que nunca poderemos realizar uma busca mais precisa depois, mesmo que isso seja desejado. Uma alternativa é armazenar duas cópias dos dados: uma quantizada e outra com precisão total. Embora isso funcione, exige armazenamento redundante. Considere um cenário em que temos `Float64` como dado original e queremos executar buscas com diferentes níveis de precisão (16 bits, 32 bits ou 64 bits completos). Precisaríamos armazenar três cópias separadas dos dados.

O ClickHouse oferece o tipo de dado Quantized Bit (`QBit`), que resolve essas limitações ao:

1. Armazenar os dados originais com precisão total.
2. Permitir que a precisão da quantização seja especificada em tempo de consulta.

Isso é feito armazenando os dados em um formato agrupado por bits (ou seja, todos os i-ésimos bits de todos os vetores são armazenados juntos), o que permite leituras apenas no nível de precisão solicitado. Assim, você obtém os benefícios de velocidade da redução de E/S e do processamento proporcionados pela quantização, ao mesmo tempo em que mantém todos os dados originais disponíveis quando necessário. Quando a precisão máxima é selecionada, a busca se torna exata.

Para declarar uma coluna do tipo `QBit`, use a seguinte sintaxe:

```sql theme={null}
column_name QBit(element_type, dimension)
```

Onde:

* `element_type` – o tipo de cada elemento do vetor. Os tipos aceitos são `BFloat16`, `Float32` e `Float64`
* `dimension` – o número de elementos em cada vetor

<div id="qbit-create">
  #### Criando uma tabela `QBit` e adicionando dados
</div>

```sql theme={null}
CREATE TABLE fruit_animal (
    word String,
    vec QBit(Float64, 5)
) ENGINE = MergeTree
ORDER BY word;

INSERT INTO fruit_animal VALUES
    ('apple', [-0.99105519, 1.28887844, -0.43526649, -0.98520696, 0.66154391]),
    ('banana', [-0.69372815, 0.25587061, -0.88226235, -2.54593015, 0.05300475]),
    ('orange', [0.93338752, 2.06571317, -0.54612565, -1.51625717, 0.69775337]),
    ('dog', [0.72138876, 1.55757105, 2.10953259, -0.33961248, -0.62217325]),
    ('cat', [-0.56611276, 0.52267331, 1.27839863, -0.59809804, -1.26721048]),
    ('horse', [-0.61435682, 0.48542571, 1.21091247, -0.62530446, -1.33082533]);
```

<div id="qbit-search">
  #### Busca vetorial com `QBit`
</div>

Vamos encontrar os vizinhos mais próximos de um vetor que representa a palavra 'lemon' usando a distância L2. O terceiro parâmetro da função de distância especifica a precisão em bits — valores mais altos oferecem mais exatidão, mas exigem mais processamento.

Você pode encontrar todas as funções de distância disponíveis para `QBit` [aqui](/pt-BR/reference/data-types/qbit#vector-search-functions).

**Busca com precisão total (64 bits):**

```sql theme={null}
SELECT
    word,
    L2DistanceTransposed(vec, [-0.88693672, 1.31532824, -0.51182908, -0.99652702, 0.59907770], 64) AS distance
FROM fruit_animal
ORDER BY distance;
```

```text theme={null}
   ┌─word───┬────────────distance─┐
1. │ apple  │ 0.14639757188169716 │
2. │ banana │   1.998961369007679 │
3. │ orange │   2.039041552613732 │
4. │ cat    │   2.752802631487914 │
5. │ horse  │  2.7555776805484813 │
6. │ dog    │   3.382295083120104 │
   └────────┴─────────────────────┘
```

**Busca de precisão reduzida:**

```sql theme={null}
SELECT
    word,
    L2DistanceTransposed(vec, [-0.88693672, 1.31532824, -0.51182908, -0.99652702, 0.59907770], 12) AS distance
FROM fruit_animal
ORDER BY distance;
```

```text theme={null}
   ┌─word───┬───────────distance─┐
1. │ apple  │  0.757668703053566 │
2. │ orange │ 1.5499475034938677 │
3. │ banana │ 1.6168396735102937 │
4. │ cat    │  2.429752230904804 │
5. │ horse  │  2.524650475528617 │
6. │ dog    │   3.17766975527459 │
   └────────┴────────────────────┘
```

Observe que, com a quantização de 12 bits, obtemos uma boa aproximação das distâncias, com execução da consulta mais rápida. A ordenação relativa permanece em grande parte consistente, com 'apple' ainda sendo a correspondência mais próxima.

<div id="qbit-performance">
  #### Considerações de desempenho
</div>

O ganho de desempenho do `QBit` vem da redução das operações de E/S, já que menos dados precisam ser lidos do armazenamento ao usar uma precisão menor. Além disso, quando o `QBit` contém dados `Float32`, se o parâmetro de precisão for 16 ou menos, há ganhos adicionais com a redução do processamento. O parâmetro de precisão controla diretamente o equilíbrio entre exatidão e velocidade:

* **Maior precisão** (mais próxima da largura original dos dados): Resultados mais exatos, consultas mais lentas
* **Menor precisão**: Consultas mais rápidas com resultados aproximados e menor uso de memória

<div id="references">
  ### Referências
</div>

Blog:

* [Busca vetorial com ClickHouse - Parte 1](https://clickhouse.com/blog/vector-search-clickhouse-p1)
* [Busca vetorial com ClickHouse - Parte 2](https://clickhouse.com/blog/vector-search-clickhouse-p2)
* [Criamos um mecanismo de busca vetorial que permite escolher a precisão em tempo de consulta](https://clickhouse.com/blog/qbit-vector-search)
