> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/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)](/docs/pt-BR/reference/data-types/array), [Array(Float32)](/docs/pt-BR/reference/data-types/array) ou [Array(BFloat16)](/docs/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](/docs/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](/docs/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](/docs/pt-BR/reference/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-data_skipping-indexes) e [aqui](/docs/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](/docs/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)](/docs/pt-BR/reference/data-types/array), [Array(Float64)](/docs/pt-BR/reference/data-types/array) ou [Array(BFloat16)](/docs/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](/docs/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](/docs/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](/docs/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](/docs/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](/docs/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](/docs/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](/docs/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](/docs/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](/docs/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.
Com a configuração `vector_search_with_rescoring = 1`, o ClickHouse lê os vetores originais com precisão total das linhas candidatas e calcula a distância final no pipeline SQL normal.
Quando o plano de consulta permite, o ClickHouse filtra a varredura para as linhas candidatas retornadas pelo índice vetorial antes do cálculo final da distância.
Essa etapa é chamada de rescoring e pode melhorar a precisão, especialmente com índices vetoriais quantizados, porque o ranking final usa os vetores armazenados em vez das distâncias do índice.
Se filtros adicionais removerem candidatos demais ou se for necessário maior recall, aumente a configuração `vector_search_index_fetch_multiplier` para que o índice vetorial retorne mais linhas candidatas para rescoring.

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](/docs/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 `_distance`.
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](/docs/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](/docs/pt-BR/concepts/features/operations/insert/inserting-data) ou durante [mesclagens](/docs/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](/docs/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](/docs/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](/docs/pt-BR/reference/settings/merge-tree-settings#materialize_skip_indexes_on_merge).
Isso, em conjunto com a instrução [ALTER TABLE \[...\] MATERIALIZE INDEX \[...\]](/docs/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](/docs/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](/docs/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](/docs/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](/docs/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](/docs/pt-BR/reference/settings/session-settings#vector_search_index_fetch_multiplier), [vector\_search\_with\_rescoring](/docs/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](/docs/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](/docs/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.
Para consultas com `vector_search_with_rescoring = 1`, o ClickHouse pode usar essas posições de linhas para filtrar linhas antes de calcular a distância final a partir dos vetores armazenados, quando o plano de consulta permite essa otimização.
Sem rescoring, o ClickHouse usa diretamente as distâncias do índice vetorial por meio da coluna virtual `_distance`.
Ambos os modos ainda usam os intervalos de grânulos subjacentes para agendar leituras, o que 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 a parte de dados 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 (parte) com linhas relevantes (há no máximo `LIMIT [N]` desses grânulos).
Com `vector_search_with_rescoring = 1`, o ClickHouse pode então ler as posições de linhas correspondentes e calcular a distância exata para essas linhas.
Com um valor pequeno de `GRANULARITY`, cada subíndice pode retornar até `LIMIT N` linhas candidatas.
Como resultado, mais linhas candidatas talvez precisem ser lidas e pós-filtradas.
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](/docs/pt-BR/get-started/sample-datasets/laion)
* [LAION-5B](/docs/pt-BR/get-started/sample-datasets/laion5b)
* [dbpedia](/docs/pt-BR/get-started/sample-datasets/dbpedia)
* [hackernews](/docs/pt-BR/get-started/sample-datasets/hacker-news-vector-search)

<div id="vector-search-with-quantized-codecs">
  ### Busca vetorial com codecs quantizados
</div>

<Note>
  O codec `Quantized` é experimental. Ative-o com `SET allow_experimental_codecs = 1`.
  Se encontrar problemas, abra uma issue no [repositório do ClickHouse](https://github.com/clickhouse/clickhouse/issues).
</Note>

<div id="quantized-codecs-introduction">
  #### Introdução
</div>

Um [índice de similaridade vetorial](#vector-similarity-index) responde a uma consulta de vizinho mais próximo percorrendo um grafo e tem desempenho muito bom quando o grafo pode ser mantido em memória.
Duas condições limitam sua aplicabilidade:

* **Escala.** O tempo para construir o grafo e a memória necessária para armazená-lo — além dos próprios vetores — passam a ser o custo predominante.
* **Filtragem.** Com um filtro `WHERE` seletivo, o percurso de grafo se torna ineficaz, porque ou não consegue alcançar o pequeno conjunto de linhas que satisfaz o predicado, ou precisa inspecionar um número desproporcional de candidatos para encontrá-las.

Uma varredura exaustiva não sofre nenhuma dessas limitações: não requer nenhuma estrutura auxiliar, as partes são mescladas por concatenação, e um filtro simplesmente reduz o número de linhas a serem varridas.
Sua única desvantagem é o volume de dados que precisa ler: uma varredura em vetores armazenados com precisão total `Float32` é dominada pela E/S de armazenamento, porque toda a coluna de vetores precisa ser lida do disco (ou do armazenamento de objetos) — no caso de uma coluna de embedding denso, essa é a maior coluna da tabela e tem baixa taxa de compressão.

O codec de coluna `Quantized` resolve essa desvantagem.
Cada vetor é armazenado duas vezes: os valores originais em precisão total, inalterados, junto com um *código quantizado* compacto em um fluxo complementar.
Uma consulta de busca vetorial primeiro varre os códigos usando uma função de distância de baixo custo, compatível com SIMD, para montar uma lista curta dos candidatos mais promissores e, em seguida, reclassifica essa lista com base nos vetores em precisão total.
Como um código ocupa apenas uma fração do tamanho do vetor bruto, a varredura dessa lista curta lê muito menos bytes do armazenamento — e acessa a coluna em precisão total apenas para o pequeno conjunto de candidatos selecionados — enquanto o ranking final permanece preciso.

<div id="quantized-codecs-declaring">
  #### Declarando o codec
</div>

Adicione um codec `Quantized(...)` a uma coluna `Array(Float32)` (ou `Array(Float64)` / `Array(BFloat16)`).
Como o codec é experimental, primeiro ative `allow_experimental_codecs`:

```sql theme={null}
SET allow_experimental_codecs = 1;

CREATE TABLE vectors
(
    id UInt32,
    vec Array(Float32) CODEC(Quantized('rabitq', 1536))
)
ENGINE = MergeTree ORDER BY id;
```

Os dados com precisão total são armazenados normalmente; o codec apenas adiciona o fluxo de códigos complementar.
O codec é definido na criação da tabela e não pode ser adicionado nem alterado com `ALTER TABLE`.

<div id="quantized-codecs-methods">
  #### Métodos de quantização
</div>

Cada método representa um ponto diferente no trade-off entre tamanho / precisão / métrica. O argumento `dimensions` é o comprimento do vetor.

* `Quantized('rabitq', dimensions)` — um bit de sinal por coordenada, mais um fator de correção de cosseno sem viés (`dimensions/8 + 4` bytes). Uma opção padrão forte, pequena e com baixo custo de `popcount`. Apenas `cosineDistance`.
* `Quantized('turboquant', dimensions)` — dois bits por coordenada (um código MSE de 1 bit e um código residual de 1 bit) para candidatos de maior fidelidade (`dimensions/4 + 4` bytes). Apenas `cosineDistance`.
* `Quantized('int8', dimensions)` — um código `Int8` por coordenada, mais a norma do vetor (`dimensions + 4` bytes); é o maior, mas também o código flat mais fiel. Suporta `L2Distance` e `cosineDistance`.
* `Quantized('prefix', dimensions, leading_dimensions, 'int8'|'bf16')` — Matryoshka: mantém apenas as `leading_dimensions` coordenadas iniciais, como `Int8` (com uma escala por vetor) ou `BFloat16`. Códigos muito pequenos para embeddings treinados com Matryoshka Representation Learning. Suporta `L2Distance` e `cosineDistance`.
* `Quantized('product', dimensions, nbits, m)` — Quantização de Produto: um codebook por partição treinado com k-means; cada vetor se torna `m` códigos de `nbits` bits (portanto, `dimensions` deve ser múltiplo de `m`). É a opção mais compacta e com maior recall por byte, ao custo de uma etapa de treinamento durante o insert. Suporta `L2Distance` e `cosineDistance`.

`rabitq` e `turboquant` exigem que `dimensions` seja múltiplo de 8.

<div id="quantized-codecs-searching">
  #### Pesquisa transparente
</div>

Não há nenhuma sintaxe de consulta especial — escreva a mesma consulta top-`k` que você usaria na [busca exata](#exact-nearest-neighbor-search):

```sql theme={null}
WITH [/* reference vector of `dimensions` floats */] AS reference_vec
SELECT id
FROM vectors
ORDER BY cosineDistance(vec, reference_vec) ASC
LIMIT 10
SETTINGS vector_search_use_quantized_codes = 1;
```

Com `vector_search_use_quantized_codes = 1`, o otimizador reescreve automaticamente a consulta em um plano de dois estágios: ele varre os códigos quantizados para montar uma lista curta e, em seguida, recalcula a pontuação dessa lista em relação ao `vec` com precisão total.
A configuração vem desativada por padrão, portanto, sem ela, a mesma consulta é executada como uma varredura exata simples — o codec nunca altera os resultados; ele apenas oferece um caminho mais rápido quando você opta por usá-lo.
Use uma função de distância compatível com o método escolhido: `cosineDistance` para todos os métodos e `L2Distance`, adicionalmente, para `int8`, `prefix` e `product`.

<div id="quantized-codecs-settings">
  #### Configurações
</div>

* `allow_experimental_codecs` — deve ser habilitado para declarar um codec `Quantized` (padrão: `0`).
* `vector_search_use_quantized_codes` — habilita a reescrita em dois estágios de lista curta e rescoring (padrão: `0`). Quando desativada, as consultas fazem uma varredura exata dos vetores em precisão total.
* `vector_search_index_fetch_multiplier` — quantos candidatos entram na lista curta em relação ao `LIMIT` da consulta: a varredura mantém os `LIMIT × multiplier` melhores códigos antes do rescoring. Valores maiores melhoram o recall, à custa de mais rescoring. O padrão é `1` (sem oversampling), portanto geralmente é necessário aumentá-lo — por exemplo, para `10` ou mais — para obter bom recall.

<div id="quantized-codecs-built-for-scale">
  #### Projetado para escalar
</div>

O codec se encaixa bem no ClickHouse porque a parte cara — a varredura — é exatamente o que o mecanismo do ClickHouse foi projetado para fazer bem:

* **Vetorizado.** Os kernels de varredura são escritos para SIMD, com encaminhamento em tempo de execução para as instruções mais largas compatíveis com a CPU: `popcount` em hardware para os métodos de código de sinal (`rabitq`, `turboquant`) e fused multiply-add largo para os demais.
* **Em paralelo entre núcleos e partes.** Uma varredura plana é trivialmente paralela, e o ClickHouse a trata assim: as distâncias são calculadas em todas as threads disponíveis e em todas as partes de uma tabela ao mesmo tempo, com apenas a mesclagem final do top-`k` sendo serializada.
* **Distribuído.** Em um cluster com shards, o trabalho é distribuído entre as máquinas — cada shard varre sua própria fatia em paralelo, e o coordenador mescla as listas curtas.
* **Colunar e compatível com filtros.** Os códigos quantizados ocupam sua própria coluna, comprimidos e lidos pelo mesmo caminho de E/S que todas as outras colunas, de modo que um `WHERE` seletivo simplesmente deixa menos códigos para varrer.
* **Sem etapa de compilação separada.** Os códigos são produzidos à medida que os vetores são gravados e mesclados por concatenação — não há índice para construir, ajustar ou reconstruir, então uma tabela está pronta para busca assim que seus dados chegam.

Os códigos servem apenas para gerar candidatos; a coluna de precisão total, mantida no lugar, fornece o ranking final preciso.

<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](/docs/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 exatidã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 exata 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[, stride])
```

Onde:

* `element_type` – o tipo de cada elemento do vetor. Os tipos aceitos são `Int8`, `BFloat16`, `Float32` e `Float64`
* `dimension` – o número de elementos em cada vetor
* `stride` – opcional. Um divisor de `dimension` que particiona as dimensões em `dimension / stride` grupos contíguos armazenados em streams separados, de modo que uma busca apenas nas dimensões iniciais leia menos streams (útil para embeddings Matryoshka). O padrão é `dimension`, caso em que o tipo é idêntico em bytes a um `QBit` sem `stride`. Consulte a [página do tipo de dado `QBit`](/docs/pt-BR/reference/data-types/qbit) para mais detalhes.

<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](/docs/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)
