> ## 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.

> Documentation sur la recherche vectorielle exacte et approximative

# Recherche vectorielle exacte et approximative

Le problème qui consiste à trouver les N points les plus proches dans un espace multidimensionnel (vectoriel) pour un point donné est appelé [recherche des plus proches voisins](https://en.wikipedia.org/wiki/Nearest_neighbor_search), ou plus simplement : recherche vectorielle.
Il existe deux grandes approches pour effectuer une recherche vectorielle :

* La recherche vectorielle exacte calcule la distance entre le point donné et tous les points de l'espace vectoriel. Cela garantit la meilleure précision possible, c'est-à-dire que les points renvoyés sont effectivement les véritables plus proches voisins. Comme l'espace vectoriel est exploré de façon exhaustive, la recherche vectorielle exacte peut être trop lente pour un usage réel.
* La recherche vectorielle approximative désigne un ensemble de techniques (par exemple, des structures de données spécialisées comme les graphes et les forêts aléatoires) qui calculent les résultats bien plus rapidement que la recherche vectorielle exacte. La précision obtenue est généralement "suffisamment bonne" pour un usage pratique. Bon nombre de techniques approximatives proposent des paramètres permettant d'ajuster le compromis entre la précision des résultats et le temps de recherche.

Une recherche vectorielle (exacte ou approximative) peut s'écrire en SQL comme suit :

```sql theme={null}
WITH [...] AS reference_vector
SELECT [...]
FROM table
WHERE [...] -- a WHERE clause is optional
ORDER BY <DistanceFunction>(vectors, reference_vector)
LIMIT <N>
```

Les points de l’espace vectoriel sont stockés dans une colonne `vectors` de type tableau, par exemple [Array(Float64)](/docs/fr/reference/data-types/array), [Array(Float32)](/docs/fr/reference/data-types/array) ou [Array(BFloat16)](/docs/fr/reference/data-types/array).
Le vecteur de référence est un tableau constant, défini comme une expression de table commune.
`<DistanceFunction>` calcule la distance entre le point de référence et tous les points stockés.
N’importe laquelle des [fonctions de distance](/docs/fr/reference/functions/regular-functions/distance-functions) disponibles peut être utilisée à cette fin.
`<N>` indique le nombre de voisins à renvoyer.

<div id="exact-nearest-neighbor-search">
  ## Recherche vectorielle exacte
</div>

Une recherche vectorielle exacte peut être effectuée en utilisant telle quelle la requête SELECT ci-dessus.
Le temps d’exécution de ces requêtes est généralement proportionnel au nombre de vecteurs stockés et à leur dimension, c’est-à-dire au nombre d’éléments du tableau.
Par ailleurs, comme ClickHouse effectue un balayage exhaustif de tous les vecteurs, le temps d’exécution dépend également du nombre de threads utilisés par la requête (voir le paramètre [max\_threads](/docs/fr/reference/settings/session-settings#max_threads)).

<div id="exact-nearest-neighbor-search-example">
  ### Exemple
</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;
```

renvoie

```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">
  ## recherche vectorielle approximative
</div>

<div id="vector-similarity-index">
  ### Index de similarité vectorielle
</div>

ClickHouse fournit un index spécial de « similarité vectorielle » permettant d’effectuer une recherche vectorielle approximative.

<Note>
  Les index de similarité vectorielle sont disponibles dans ClickHouse version 25.8 et les versions ultérieures.
  Si vous rencontrez des problèmes, veuillez ouvrir une issue dans le [dépôt ClickHouse](https://github.com/clickhouse/clickhouse/issues).
</Note>

<div id="creating-a-vector-similarity-index">
  #### Création d’un index de similarité vectorielle
</div>

Un index de similarité vectorielle peut être créé sur une nouvelle table comme suit :

```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 [...]
```

Vous pouvez également ajouter un index de similarité vectorielle à une table existante :

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

Les index de similarité vectorielle sont des types particuliers d’index de saut de données (voir [ici](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-data_skipping-indexes) et [ici](/docs/fr/concepts/features/performance/skip-indexes/skipping-indexes)).
Par conséquent, l’instruction `ALTER TABLE` ci-dessus ne construit l’index que pour les nouvelles données qui seront insérées dans la table.
Pour construire également l’index pour les données existantes, vous devez le matérialiser :

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

La fonction `<distance_function>` doit être

* `L2Distance`, la [distance euclidienne](https://en.wikipedia.org/wiki/Euclidean_distance), qui représente la longueur du segment entre deux points dans l'espace euclidien,
* `cosineDistance`, la [distance cosinus](https://en.wikipedia.org/wiki/Cosine_similarity#Cosine_distance), qui représente l'angle entre deux vecteurs non nuls, ou
* `dotProduct`, le [produit scalaire](https://en.wikipedia.org/wiki/Dot_product) (produit intérieur), qui représente la somme des produits élément par élément de deux vecteurs. Équivalent à `cosineDistance` sur des données normalisées.

Pour les données normalisées, `L2Distance` est généralement le meilleur choix ; sinon, `cosineDistance` est recommandé pour compenser les différences d'échelle.

<Note>
  Pour les fonctions de distance `L2Distance` et `cosineDistance`, une valeur plus faible indique une similarité plus élevée, tandis que pour `dotProduct`, une valeur plus élevée indique une similarité plus élevée.
  Par conséquent, les index vectoriels avec `L2Distance` et `cosineDistance` ne peuvent être utilisés que par des requêtes `SELECT [...] ORDER BY [...] ASC` (`ASC` est la valeur par défaut de `ORDER BY`), tandis que les index vectoriels construits pour `dotProduct` ne peuvent être utilisés que par des requêtes `SELECT [...] ORDER BY [...] DESC`.
</Note>

`<dimensions>` spécifie la cardinalité du tableau (nombre d'éléments) dans la colonne sous-jacente.
Si ClickHouse trouve un tableau avec une cardinalité différente lors de la création de l'index, l'index est abandonné et une erreur est renvoyée.

Le paramètre facultatif GRANULARITY `<N>` fait référence à la taille des granules d'index (voir [ici](/docs/fr/concepts/features/performance/skip-indexes/skipping-indexes)).
Contrairement aux index de saut classiques, qui utilisent une granularité d'index par défaut de 1, les index de similarité vectorielle utilisent 100 millions comme granularité d'index par défaut.
Cette valeur garantit que seul un petit nombre d'index sont construits en interne, même pour de grandes parts.
Nous recommandons de ne modifier la granularité d'index qu'aux utilisateurs avancés qui comprennent les implications de ce qu'ils font (voir [ci-dessous](#differences-to-regular-skipping-indexes)).

Les index de similarité vectorielle sont génériques, dans le sens où ils peuvent prendre en charge différentes méthodes de recherche approximative.
La méthode effectivement utilisée est spécifiée par le paramètre `<type>`.
À ce jour, la seule méthode disponible est HNSW ([article académique](https://arxiv.org/abs/1603.09320)), une technique populaire et de pointe de recherche vectorielle approximative basée sur des graphes de proximité hiérarchiques.
Si HNSW est utilisé comme type, les utilisateurs peuvent éventuellement spécifier des paramètres supplémentaires propres à 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 [...]
```

Les paramètres spécifiques à HNSW suivants sont disponibles :

* `<quantization>` contrôle la quantification des vecteurs dans le graphe de proximité. Les valeurs possibles sont `f64`, `f32`, `f16`, `bf16`, `i8` ou `b1`. La valeur par défaut est `bf16`. Notez que ce paramètre n’affecte pas la représentation des vecteurs dans la colonne source.
* `<hnsw_max_connections_per_layer>` contrôle le nombre de voisins par nœud du graphe, également appelé hyperparamètre HNSW `M`. La valeur par défaut est `32`. La valeur `0` signifie que la valeur par défaut est utilisée.
* `<hnsw_candidate_list_size_for_construction>` contrôle la taille de la liste dynamique de candidats lors de la construction du graphe HNSW, également appelée hyperparamètre HNSW `ef_construction`. La valeur par défaut est `128`. La valeur `0` signifie que la valeur par défaut est utilisée.

Les valeurs par défaut de tous les paramètres spécifiques à HNSW fonctionnent correctement dans la grande majorité des cas d’usage.
Nous ne recommandons donc pas de personnaliser les paramètres spécifiques à HNSW.

D’autres restrictions s’appliquent :

* Les index de similarité vectorielle ne peuvent être construits que sur des colonnes de type [Array(Float32)](/docs/fr/reference/data-types/array), [Array(Float64)](/docs/fr/reference/data-types/array) ou [Array(BFloat16)](/docs/fr/reference/data-types/array). Les tableaux de flottants nullable ou à faible cardinalité, tels que `Array(Nullable(Float32))` et `Array(LowCardinality(Float32))`, ne sont pas autorisés.
* Les index de similarité vectorielle doivent être construits sur une seule colonne.
* Les index de similarité vectorielle peuvent être construits sur des expressions calculées (par exemple, `INDEX index_name arraySort(vectors) TYPE vector_similarity([...])`), mais ces index ne pourront pas être utilisés ensuite pour la recherche approximative de voisins.
* Les index de similarité vectorielle exigent que tous les tableaux de la colonne source contiennent `<dimension>` éléments ; cela est vérifié lors de la création de l’index. Pour détecter les violations de cette exigence le plus tôt possible, les utilisateurs peuvent ajouter une [contrainte](/docs/fr/reference/statements/create/table#constraints) sur la colonne vectorielle, par exemple `CONSTRAINT same_length CHECK length(vectors) = 256`.
* De même, les valeurs de tableau dans la colonne source ne doivent pas être vides (`[]`) ni avoir la valeur par défaut (également `[]`).

**Estimation de la consommation de stockage et de mémoire**

Un vecteur généré pour être utilisé avec un modèle d’IA classique (par exemple un Large Language Model, [LLM](https://en.wikipedia.org/wiki/Large_language_model)) se compose de centaines, voire de milliers, de valeurs en virgule flottante.
Ainsi, une seule valeur vectorielle peut consommer plusieurs kilo-octets de mémoire.
Les utilisateurs qui souhaitent estimer l’espace de stockage requis pour la colonne vectorielle source dans la table, ainsi que la mémoire vive nécessaire pour l’index de similarité vectorielle, peuvent utiliser les deux formules ci-dessous :

Consommation de stockage de la colonne vectorielle dans la table (non compressée) :

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

Exemple avec le [jeu de données dbpedia](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M) :

```text theme={null}
Storage consumption = 1 million * 1536 * 4 (for Float32) = 6.1 GB
```

L’index de similarité vectorielle doit être entièrement chargé du disque vers la mémoire principale pour exécuter les recherches.
De même, l’index vectoriel est lui aussi entièrement construit en mémoire, puis enregistré sur disque.

Consommation mémoire requise pour charger un index vectoriel :

```text theme={null}
Memory for vectors in the index (mv) = Number of vectors * Dimension * Size of quantized data type
Memory for in-memory graph (mg) = Number of vectors * hnsw_max_connections_per_layer * Bytes_per_node_id (= 4) * Layer_node_repetition_factor (= 2)

Memory consumption: mv + mg
```

Exemple avec le [jeu de données dbpedia](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M) :

```text theme={null}
Memory for vectors in the index (mv) = 1 million * 1536 * 2 (for BFloat16) = 3072 MB
Memory for in-memory graph (mg) = 1 million * 64 * 2 * 4 = 512 MB

Memory consumption = 3072 + 512 = 3584 MB
```

La formule ci-dessus ne prend pas en compte la mémoire supplémentaire dont les indexes de similarité vectorielle ont besoin pour allouer des structures de données d’exécution, comme des tampons préalloués et des caches.

<div id="using-a-vector-similarity-index">
  #### Utilisation d'un index de similarité vectorielle
</div>

<Note>
  Pour utiliser les index de similarité vectorielle, le paramètre [compatibility](/docs/fr/reference/settings/session-settings) doit être défini sur `''` (la valeur par défaut), `'25.1'` ou une version ultérieure.
</Note>

Les index de similarité vectorielle prennent en charge les requêtes SELECT de la forme suivante :

```sql theme={null}
WITH [...] AS reference_vector
SELECT [...]
FROM table
WHERE [...] -- a WHERE clause is optional
ORDER BY <DistanceFunction>(vectors, reference_vector)
LIMIT <N>
```

L'optimiseur de requêtes de ClickHouse tente de faire correspondre le modèle de requête ci-dessus et d'exploiter les vector similarity indexes disponibles.
Une requête ne peut utiliser un vector similarity index que si la fonction de distance dans la requête SELECT est identique à celle utilisée dans la définition de l'index.

Les utilisateurs avancés peuvent fournir une valeur personnalisée pour le paramètre [hnsw\_candidate\_list\_size\_for\_search](/docs/fr/reference/settings/session-settings#hnsw_candidate_list_size_for_search) (également connu sous le nom d'hyperparamètre HNSW "ef\_search") afin d'ajuster la taille de la liste de candidats lors de la recherche (par exemple, `SELECT [...] SETTINGS hnsw_candidate_list_size_for_search = <value>`).
La valeur par défaut du paramètre, 256, convient à la grande majorité des cas d'usage.
Des valeurs plus élevées améliorent la précision au détriment des performances.

Si la requête peut utiliser un index de similarité vectorielle, ClickHouse vérifie que la valeur LIMIT `<N>` fournie dans les requêtes SELECT est dans des limites raisonnables.
Plus précisément, une erreur est renvoyée si `<N>` est supérieur à la valeur du paramètre [max\_limit\_for\_vector\_search\_queries](/docs/fr/reference/settings/session-settings#max_limit_for_vector_search_queries), dont la valeur par défaut est 100.
Des valeurs LIMIT trop élevées peuvent ralentir les recherches et indiquent généralement une erreur d'utilisation.

Pour vérifier si une requête SELECT utilise un index de similarité vectorielle, vous pouvez la préfixer avec `EXPLAIN indexes = 1`.

Par exemple, interrogez

```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;
```

peut retourner

```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                                                                    │
    └─────────────────────────────────────────────────────────────────────────────────────────────────┘
```

Dans cet exemple, 1 million de vecteurs issus du [dbpedia dataset](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M), chacun de dimension 1536, sont stockés dans 575 granules, soit 1,7k lignes par granule.
La requête demande 10 voisins et le vector similarity index les identifie dans 10 granules distincts.
Ces 10 granules seront lus lors de l'exécution de la requête.

Les index de similarité vectorielle sont utilisés si la sortie contient `Skip` ainsi que le nom et le type de l'index vectoriel (dans l'exemple, `idx` et `vector_similarity`).
Dans ce cas, l'index de similarité vectorielle a éliminé deux des quatre granules, soit 50 % des données.
Plus le nombre de granules pouvant être éliminés est important, plus l'utilisation de l'index est efficace.

<Tip>
  Pour forcer l’utilisation de l’index, vous pouvez exécuter la requête SELECT avec le paramètre [force\_data\_skipping\_indexes](/docs/fr/reference/settings/session-settings#force_data_skipping_indices) (indiquez le nom de l’index comme valeur du paramètre).
</Tip>

**Post-filtrage et pré-filtrage**

Les utilisateurs peuvent éventuellement spécifier une clause `WHERE` avec des conditions de filtre supplémentaires pour la requête SELECT.
ClickHouse évaluera ces conditions de filtre selon une stratégie de post-filtrage ou de pré-filtrage.
En résumé, les deux stratégies déterminent l'ordre dans lequel les filtres sont évalués :

* Le post-filtrage signifie que l’index de similarité vectorielle est évalué en premier, puis que ClickHouse évalue le ou les filtres supplémentaires spécifiés dans la clause `WHERE`.
* Le pré-filtrage signifie que l’ordre d’évaluation des filtres est inverse.

Ces stratégies présentent des compromis différents :

* Le post-filtrage présente un problème général : il peut renvoyer moins de lignes que le nombre demandé dans la clause `LIMIT <N>`. Cette situation se produit lorsqu’une ou plusieurs lignes de résultat renvoyées par l’index de similarité vectorielle ne satisfont pas les filtres supplémentaires.
* Le pré-filtrage est généralement un problème non résolu. Certaines bases de données vectorielles spécialisées proposent des algorithmes de pré-filtrage, mais la plupart des bases de données relationnelles (y compris ClickHouse) reviennent à une recherche exacte des voisins, c’est-à-dire à un balayage brute-force sans index.

La stratégie utilisée dépend de la condition de filtrage.

*Les filtres supplémentaires font partie de la clé de partitionnement*

Si la condition de filtrage supplémentaire fait partie de la clé de partitionnement, ClickHouse appliquera alors l’élagage des partitions.
Par exemple, une table est partitionnée par plage sur la colonne `year` et la requête suivante est exécutée :

```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;
```

ClickHouse ignorera toutes les partitions sauf celle de 2025.

*Les filtres supplémentaires ne peuvent pas être évalués à l’aide des index*

Si les conditions de filtre supplémentaires ne peuvent pas être évaluées à l’aide des index (index de clé primaire, index de saut de données), ClickHouse appliquera un post-filtrage.

*Les filtres supplémentaires peuvent être évalués à l’aide de l’index de clé primaire*

Si les conditions de filtre supplémentaires peuvent être évaluées à l’aide de la [clé primaire](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree#primary-key) (c’est-à-dire qu’elles forment un préfixe de la clé primaire) et

* la condition de filtre élimine au moins une ligne dans une partie, ClickHouse basculera vers le préfiltrage pour les plages « survivantes » au sein de la partie,
* la condition de filtre n’élimine aucune ligne dans une partie, ClickHouse effectuera un post-filtrage pour la partie.

En pratique, ce dernier cas est plutôt peu probable.

*Les filtres supplémentaires peuvent être évalués à l’aide d’un index de saut de données*

Si les conditions de filtre supplémentaires peuvent être évaluées à l’aide des [index de saut de données](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-data_skipping-indexes) (index minmax, index set, etc.), ClickHouse effectue un post-filtrage.
Dans ce cas, l’index de similarité vectorielle est évalué en premier, car il est censé éliminer plus de lignes que les autres index de saut de données.

Pour un contrôle plus fin entre post-filtrage et préfiltrage, deux paramètres peuvent être utilisés :

Le paramètre [vector\_search\_filter\_strategy](/docs/fr/reference/settings/session-settings#vector_search_filter_strategy) (par défaut : `auto`, qui implémente les heuristiques ci-dessus) peut être défini sur `prefilter`.
Cela est utile pour forcer le préfiltrage lorsque les conditions de filtre supplémentaires sont extrêmement sélectives.
Par exemple, la requête suivante peut bénéficier du préfiltrage :

```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
```

En supposant que seul un très petit nombre de livres coûtent moins de 2 dollars, le post-filtrage peut ne renvoyer aucune ligne, car les 10 meilleures correspondances renvoyées par l’index vectoriel peuvent toutes avoir un prix supérieur à 2 dollars.
En forçant le pré-filtrage (ajoutez `SETTINGS vector_search_filter_strategy = 'prefilter'` à la requête), ClickHouse trouve d’abord tous les livres dont le prix est inférieur à 2 dollars, puis exécute une recherche vectorielle brute-force sur les livres trouvés.

Autre approche pour résoudre le problème ci-dessus : configurer le paramètre [vector\_search\_index\_fetch\_multiplier](/docs/fr/reference/settings/session-settings#vector_search_index_fetch_multiplier) (par défaut : `1.0`, maximum : `1000.0`) sur une valeur > `1.0` (par exemple, `2.0`).
Le nombre de plus proches voisins récupérés depuis l’index vectoriel est multiplié par la valeur du paramètre, puis le filtre supplémentaire est appliqué à ces lignes afin de renvoyer jusqu’à LIMIT lignes.
Par exemple, nous pouvons exécuter à nouveau la requête, mais avec le multiplicateur `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;
```

ClickHouse récupérera 3,0 x 10 = 30 plus proches voisins dans l’index vectoriel de chaque fragment, puis appliquera les filtres supplémentaires.
Seuls les dix voisins les plus proches seront renvoyés.
À noter que le paramètre `vector_search_index_fetch_multiplier` peut atténuer ce problème, mais dans des cas extrêmes (condition WHERE très sélective), il reste possible que moins de N lignes demandées soient renvoyées.

**Réévaluation du score**

Les skip indexes dans ClickHouse filtrent généralement au niveau de la granule, c.-à-d. qu’une recherche dans un skip index renvoie (en interne) une liste de granules potentiellement correspondantes, ce qui réduit la quantité de données lues lors de l’analyse qui suit.
Cela fonctionne bien pour les skip indexes en général, mais dans le cas des index de similarité vectorielle, cela crée un "décalage de granularité".
Plus précisément, l’index de similarité vectorielle détermine les numéros de ligne des N vecteurs les plus similaires pour un vecteur de référence donné.
Avec le paramètre `vector_search_with_rescoring = 1`, ClickHouse lit les vecteurs d’origine en pleine précision pour les lignes candidates et calcule la distance finale dans le pipeline SQL classique.
Lorsque le plan de requête le permet, ClickHouse limite l’analyse aux lignes candidates renvoyées par l’index vectoriel avant le calcul final de la distance.
Cette étape est appelée rescoring et peut améliorer la précision, en particulier avec les index vectoriels quantifiés, car le classement final utilise les vecteurs stockés au lieu des distances de l’index.
Si des filtres supplémentaires éliminent trop de candidats ou si un meilleur rappel est nécessaire, augmentez le paramètre `vector_search_index_fetch_multiplier` afin que l’index vectoriel renvoie davantage de lignes candidates pour le rescoring.

ClickHouse propose donc une optimisation qui désactive le rescoring et renvoie directement depuis l’index les vecteurs les plus similaires ainsi que leurs distances.
Cette optimisation est activée par défaut, voir le paramètre [vector\_search\_with\_rescoring](/docs/fr/reference/settings/session-settings#vector_search_with_rescoring).
Dans les grandes lignes, son fonctionnement est le suivant : ClickHouse met à disposition les vecteurs les plus similaires et leurs distances sous la forme d’une colonne virtuelle `_distance`.
Pour le constater, exécutez une requête de recherche vectorielle avec `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>
  Une requête exécutée sans rescoring (`vector_search_with_rescoring = 0`) et avec les réplicas parallèles activés peut revenir au rescoring.
</Note>

<div id="performance-tuning">
  #### Optimisation des performances
</div>

**Réglage de la compression**

Dans pratiquement tous les cas d’usage, les vecteurs de la colonne sous-jacente sont denses et se compressent mal.
Par conséquent, la [compression](/docs/fr/reference/statements/create/table#column_compression_codec) ralentit les insertions et les lectures de la colonne vectorielle.
Nous recommandons donc de désactiver la compression.
Pour ce faire, spécifiez `CODEC(NONE)` pour la colonne vectorielle comme ceci :

```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;
```

**Optimisation de la création des index**

Le cycle de vie des index de similarité vectorielle est lié à celui des parties.
Autrement dit, chaque fois qu'une nouvelle partie comportant un index de similarité vectorielle défini est créée, l'index l'est aussi.
Cela se produit généralement lorsque des données sont [insérées](/docs/fr/concepts/features/operations/insert/inserting-data) ou lors des [fusions](/docs/fr/concepts/core-concepts/merges).
Malheureusement, HNSW est connu pour ses temps de création d'index élevés, ce qui peut considérablement ralentir les insertions et les fusions.
Dans l'idéal, les index de similarité vectorielle ne doivent être utilisés que si les données sont immuables ou rarement modifiées.

Pour accélérer la création des index, les techniques suivantes peuvent être utilisées :

Premièrement, la création des index peut être parallélisée.
Le nombre maximal de threads de création d'index peut être configuré à l'aide du paramètre serveur [max\_build\_vector\_similarity\_index\_thread\_pool\_size](/docs/fr/reference/settings/server-settings/settings#max_build_vector_similarity_index_thread_pool_size).
Pour des performances optimales, la valeur du paramètre doit être réglée sur le nombre de cœurs CPU.

Deuxièmement, pour accélérer les instructions INSERT, les utilisateurs peuvent désactiver la création des index de saut sur les parties nouvellement insérées à l'aide du paramètre de session [materialize\_skip\_indexes\_on\_insert](/docs/fr/reference/settings/session-settings#materialize_skip_indexes_on_insert).
Les requêtes SELECT sur ces parties reviendront alors à une recherche exacte.
Comme les parties insérées ont tendance à être petites par rapport à la taille totale de la table, l'impact sur les performances devrait être négligeable.

Troisièmement, pour accélérer les fusions, les utilisateurs peuvent désactiver la création des index de saut sur les parties fusionnées à l'aide du paramètre de session [materialize\_skip\_indexes\_on\_merge](/docs/fr/reference/settings/merge-tree-settings#materialize_skip_indexes_on_merge).
Cela, associé à l'instruction [ALTER TABLE \[...\] MATERIALIZE INDEX \[...\]](/docs/fr/reference/statements/alter/skipping-index#materialize-index), fournit un contrôle explicite sur le cycle de vie des index de similarité vectorielle.
Par exemple, la création des index peut être différée jusqu'à ce que toutes les données aient été ingérées, ou jusqu'à une période de faible charge du système, comme le week-end.

**Optimisation de l'utilisation des index**

Les requêtes SELECT doivent charger les index de similarité vectorielle en mémoire principale pour pouvoir les utiliser.
Pour éviter qu'un même index de similarité vectorielle soit chargé de façon répétée en mémoire principale, ClickHouse fournit un cache en mémoire dédié à ces index.
Plus ce cache est grand, moins il y aura de chargements inutiles.
La taille maximale du cache peut être configurée à l'aide du paramètre serveur [vector\_similarity\_index\_cache\_size](/docs/fr/reference/settings/server-settings/settings#vector_similarity_index_cache_size).
Par défaut, le cache peut atteindre 5 Go.

Les messages de journal suivants (`system.text_log`) indiquent que l'index de similarité vectorielle est en cours de chargement.
Si de tels messages apparaissent de façon répétée pour différentes requêtes de recherche vectorielle, cela indique que la taille du cache est trop faible.

```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>
  Le cache de l’index de similarité vectorielle stocke des granules d’index vectoriel.
  Si la taille de chaque granule d’index vectoriel dépasse celle du cache, elle ne sera pas mise en cache.
  Veillez donc à calculer la taille de l’index vectoriel (à partir de la formule indiquée dans "Estimation de la consommation du stockage et de la mémoire" ou [system.data\_skipping\_indices](/docs/fr/reference/system-tables/data_skipping_indices)) et à dimensionner le cache en conséquence.
</Note>

*Nous rappelons que la vérification du cache de l’index vectoriel et, si nécessaire, son augmentation doivent constituer la première étape lors de l’analyse de requêtes de recherche vectorielle lentes.*

La taille actuelle du cache de l’index de similarité vectorielle est indiquée dans [system.metrics](/docs/fr/reference/system-tables/metrics) :

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

Les succès et échecs du cache pour une requête avec un certain identifiant de requête peuvent être obtenus à partir de [system.query\_log](/docs/fr/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;
```

Pour les cas d’usage en production, nous recommandons de dimensionner le cache de façon à ce que tous les index vectoriels restent en mémoire en permanence.

**Réglage de la quantification**

La [quantification](https://huggingface.co/blog/embedding-quantization) est une technique qui permet de réduire l’empreinte mémoire des vecteurs ainsi que les coûts de calcul liés à la construction et au parcours des index vectoriels.
Les index vectoriels de ClickHouse prennent en charge les options de quantification suivantes :

| Quantification    | Nom                          | Stockage par dimension |
| ----------------- | ---------------------------- | ---------------------- |
| f32               | Simple précision             | 4 octets               |
| f16               | Demi-précision               | 2 octets               |
| bf16 (par défaut) | Demi-précision (brain float) | 2 octets               |
| i8                | Quart de précision           | 1 octet                |
| b1                | Binaire                      | 1 bit                  |

La quantification réduit la précision des recherches vectorielles par rapport à une recherche sur les valeurs d’origine en virgule flottante en pleine précision (`f32`).
Cependant, sur la plupart des jeux de données, la quantification en brain float demi-précision (`bf16`) entraîne une perte de précision négligeable ; c’est pourquoi les index de similarité vectorielle utilisent cette technique par défaut.
La quantification en quart de précision (`i8`) et la quantification binaire (`b1`) entraînent une perte de précision notable dans les recherches vectorielles.
Nous ne recommandons ces deux quantifications que si la taille de l’index de similarité vectorielle dépasse nettement la taille de DRAM disponible.
Dans ce cas, nous suggérons également d’activer le rescoring ([vector\_search\_index\_fetch\_multiplier](/docs/fr/reference/settings/session-settings#vector_search_index_fetch_multiplier), [vector\_search\_with\_rescoring](/docs/fr/reference/settings/session-settings#vector_search_with_rescoring)) afin d’améliorer la précision.
La quantification binaire n’est recommandée que 1) pour des embeddings normalisés (c.-à-d. longueur du vecteur = 1, les modèles OpenAI sont généralement normalisés), et 2) si la distance cosinus est utilisée comme fonction de distance.
En interne, la quantification binaire utilise la distance de Hamming pour construire et parcourir le graphe de proximité.
L’étape de rescoring utilise les vecteurs d’origine en pleine précision stockés dans la table pour identifier les plus proches voisins via la distance cosinus.

**Réglage du transfert de données**

Le vecteur de référence dans une requête de recherche vectorielle est fourni par l’utilisateur et est généralement obtenu via un appel à un Large Language Model (LLM).
Voici à quoi pourrait ressembler un code Python typique exécutant une recherche vectorielle dans ClickHouse

```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)
```

Les vecteurs d’embedding (`search_v` dans l’extrait ci-dessus) peuvent avoir un très grand nombre de dimensions.
Par exemple, OpenAI fournit des modèles qui génèrent des vecteurs d’embedding à 1536, voire 3072 dimensions.
Dans le code ci-dessus, le driver Python de ClickHouse remplace le vecteur d’embedding par une chaîne lisible, puis envoie la requête SELECT entièrement sous forme de chaîne.
En supposant que le vecteur d’embedding se compose de 1536 valeurs en virgule flottante simple précision, la chaîne envoyée atteint une longueur de 20 kB.
Cela entraîne une forte utilisation du CPU pour la tokenisation, l’analyse syntaxique et l’exécution de milliers de conversions de chaînes en nombres à virgule flottante.
En outre, un espace important est requis dans le fichier journal du serveur ClickHouse, ce qui entraîne également un gonflement de `system.query_log`.

Notez que la plupart des modèles de LLM renvoient un vecteur d’embedding sous la forme d’une liste ou d’un tableau NumPy de flottants natifs.
Nous recommandons donc aux applications Python de lier le paramètre du vecteur de référence sous forme binaire en utilisant le style suivant :

```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)
```

Dans l’exemple, le vecteur de référence est envoyé tel quel sous forme binaire, puis réinterprété en tableau de nombres à virgule flottante sur le serveur.
Cela réduit le temps CPU côté serveur et évite de surcharger les logs du serveur ainsi que `system.query_log`.

<div id="administration">
  #### Administration et surveillance
</div>

La taille sur disque des index de similarité vectorielle peut être obtenue à partir de [system.data\_skipping\_indices](/docs/fr/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';
```

Exemple de sortie :

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

<div id="differences-to-regular-skipping-indexes">
  #### Différences par rapport aux index de saut de données classiques
</div>

Comme tous les [index de saut de données](/docs/fr/concepts/features/performance/skip-indexes/skipping-indexes) classiques, les index de similarité vectorielle sont construits sur des granules, et chaque bloc indexé se compose de `GRANULARITY = [N]` granules (`[N]` = 1 par défaut pour les index de saut de données classiques).
Par exemple, si la granularité de l'index primaire de la table est de 8192 (paramètre `index_granularity = 8192`) et que `GRANULARITY = 2`, alors chaque bloc indexé contiendra 16384 lignes.
Cependant, les structures de données et les algorithmes de recherche approximative de voisins sont intrinsèquement orientés lignes.
Ils stockent une représentation compacte d'un ensemble de lignes et renvoient également des lignes pour les requêtes de recherche vectorielle.
Cela entraîne des différences parfois peu intuitives dans le comportement des index de similarité vectorielle par rapport aux index de saut de données classiques.

Lorsqu'un utilisateur définit un index de similarité vectorielle sur une colonne, ClickHouse crée en interne un « sous-index » de similarité vectorielle pour chaque bloc d'index.
Le sous-index est « local » en ce sens qu'il ne connaît que les lignes du bloc d'index auquel il appartient.
Dans l'exemple précédent, en supposant qu'une colonne comporte 65536 lignes, on obtient quatre blocs d'index (couvrant huit granules) et un sous-index de similarité vectorielle pour chaque bloc d'index.
En théorie, un sous-index peut renvoyer directement les lignes contenant les N points les plus proches dans son bloc d'index.
Pour les requêtes avec `vector_search_with_rescoring = 1`, ClickHouse peut utiliser ces positions de lignes pour filtrer les lignes avant de calculer la distance finale à partir des vecteurs stockés lorsque le plan de requête permet cette optimisation.
Sans rescoring, ClickHouse utilise directement les distances de l'index vectoriel via la colonne virtuelle `_distance`.
Les deux modes utilisent toujours les plages de granules environnantes pour planifier les lectures, ce qui diffère des index de saut de données classiques, qui sautent des données à la granularité des blocs d'index.

Le paramètre `GRANULARITY` détermine combien de sous-index de similarité vectorielle sont créés.
Des valeurs `GRANULARITY` plus élevées signifient des sous-index de similarité vectorielle moins nombreux, mais plus grands, jusqu'au point où une colonne (ou une data part de colonne) ne possède plus qu'un seul sous-index.
Dans ce cas, le sous-index a une vue « globale » de toutes les lignes de la colonne et peut renvoyer directement tous les granules de la colonne (part) contenant des lignes pertinentes (il y a au plus `LIMIT [N]` granules de ce type).
Avec `vector_search_with_rescoring = 1`, ClickHouse peut alors lire les positions des lignes correspondantes et calculer la distance exacte pour ces lignes.
Avec une petite valeur de `GRANULARITY`, chaque sous-index peut renvoyer jusqu'à `LIMIT N` lignes candidates.
Par conséquent, davantage de lignes candidates peuvent devoir être lues puis post-filtrées.
Notez que, dans les deux cas, la précision de la recherche est équivalente ; seule la performance de traitement diffère.
Il est généralement recommandé d'utiliser une valeur élevée de `GRANULARITY` pour les index de similarité vectorielle et de revenir à des valeurs plus faibles uniquement en cas de problèmes, comme une consommation mémoire excessive des structures de similarité vectorielle.
Si aucune valeur de `GRANULARITY` n'a été spécifiée pour les index de similarité vectorielle, la valeur par défaut est de 100 millions.

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

Requêtes :

```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] │
   └────┴─────────┘
```

Autres jeux de données d’exemple pour la recherche vectorielle approximative :

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

<div id="vector-search-with-quantized-codecs">
  ### Recherche vectorielle avec des codecs quantifiés
</div>

<Note>
  Le codec `Quantized` est expérimental. Activez-le avec `SET allow_experimental_codecs = 1`.
  Si vous rencontrez des problèmes, veuillez ouvrir une issue sur le [dépôt ClickHouse](https://github.com/clickhouse/clickhouse/issues).
</Note>

<div id="quantized-codecs-introduction">
  #### Introduction
</div>

Un [index de similarité vectorielle](#vector-similarity-index) répond à une requête de plus proche voisin en parcourant un graphe et offre d’excellentes performances lorsque ce graphe peut être maintenu en mémoire.
Deux facteurs limitent toutefois son utilisation :

* **Scale.** Le temps nécessaire pour construire le graphe et la mémoire requise pour le stocker — en plus des vecteurs eux-mêmes — deviennent le coût principal.
* **Filtering.** Avec un filtre `WHERE` sélectif, le parcours du graphe devient inefficace, car soit il ne peut pas atteindre le petit ensemble de lignes qui satisfont le prédicat, soit il doit examiner un nombre disproportionné de candidats pour les trouver.

Un balayage exhaustif n’est soumis à aucune de ces limites : il ne nécessite aucune structure auxiliaire, les parts fusionnent par concaténation, et un filtre réduit simplement le nombre de lignes à balayer.
Son seul inconvénient est le volume de données à lire : un balayage sur des vecteurs stockés en `Float32` à pleine précision est dominé par les E/S de stockage, car toute la colonne de vecteurs doit être lue depuis le disque (ou le stockage objet) — et, pour une colonne d’embeddings dense, il s’agit de la plus grande colonne de la table, qui se compresse mal.

Le codec de colonne `Quantized` répond à cet inconvénient.
Chaque vecteur est stocké deux fois : les valeurs d’origine à pleine précision, inchangées, ainsi qu’un *code quantifié* compact dans un flux associé.
Une requête de recherche vectorielle commence par balayer les codes à l’aide d’une fonction de distance peu coûteuse et adaptée au SIMD afin de constituer une liste restreinte des candidats les plus prometteurs, puis reclasse cette liste en la comparant aux vecteurs à pleine précision.
Comme un code ne représente qu’une fraction de la taille du vecteur brut, le balayage de cette liste restreinte lit bien moins d’octets depuis le stockage — et n’accède à la colonne à pleine précision que pour la poignée de candidats présélectionnés — tandis que le classement final reste précis.

<div id="quantized-codecs-declaring">
  #### Déclarer le codec
</div>

Ajoutez un codec `Quantized(...)` à une colonne `Array(Float32)` (ou `Array(Float64)` / `Array(BFloat16)`).
Ce codec est expérimental ; activez donc d’abord `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;
```

Les données en pleine précision sont stockées comme d’habitude ; le codec ajoute uniquement le flux de codes associé.
Le codec est défini lors de la création de la table et ne peut pas être ajouté ni modifié avec `ALTER TABLE`.

<div id="quantized-codecs-methods">
  #### Méthodes de quantification
</div>

Chaque méthode représente un compromis différent entre taille, précision et métrique. L'argument `dimensions` correspond à la longueur du vecteur.

* `Quantized('rabitq', dimensions)` — un bit de signe par coordonnée, plus un facteur de correction du cosinus non biaisé (`dimensions/8 + 4` octets). Une option par défaut solide, compacte et peu coûteuse en `popcount`. `cosineDistance` uniquement.
* `Quantized('turboquant', dimensions)` — deux bits par coordonnée (un code MSE sur 1 bit et un code résiduel sur 1 bit) pour des candidats plus fidèles (`dimensions/4 + 4` octets). `cosineDistance` uniquement.
* `Quantized('int8', dimensions)` — un code `Int8` par coordonnée, plus la norme du vecteur (`dimensions + 4` octets) ; c'est le code plat le plus volumineux, mais aussi le plus fidèle. Prend en charge `L2Distance` et `cosineDistance`.
* `Quantized('prefix', dimensions, leading_dimensions, 'int8'|'bf16')` — Matryoshka : ne conserve que les `leading_dimensions` premières coordonnées, en `Int8` (avec un facteur d'échelle par vecteur) ou en `BFloat16`. Des codes minuscules pour les embeddings entraînés avec Matryoshka Representation Learning. Prend en charge `L2Distance` et `cosineDistance`.
* `Quantized('product', dimensions, nbits, m)` — quantification de produit : un dictionnaire par partie entraîné avec k-means ; chaque vecteur devient `m` codes de `nbits` bits (`dimensions` doit donc être un multiple de `m`). L'option la plus compacte et celle qui offre le meilleur rappel par octet, au prix d'une étape d'entraînement lors de l'insertion. Prend en charge `L2Distance` et `cosineDistance`.

`rabitq` et `turboquant` nécessitent que `dimensions` soit un multiple de 8.

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

Il n’existe pas de syntaxe de requête particulière : écrivez la même requête top-`k` que celle que vous utiliseriez pour la [recherche exacte](#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;
```

Avec `vector_search_use_quantized_codes = 1`, l’optimiseur réécrit automatiquement la requête en un plan en deux étapes : il parcourt les codes quantifiés pour constituer une liste restreinte, puis réévalue cette liste par rapport au `vec` en pleine précision.
Le paramètre est désactivé par défaut. Sans lui, la même requête s’exécute donc comme un simple parcours exact — le codec ne modifie jamais les résultats, il offre seulement un chemin plus rapide lorsque vous l’activez.
Utilisez une fonction de distance prise en charge par la méthode choisie : `cosineDistance` pour toutes les méthodes, `L2Distance` en plus pour `int8`, `prefix` et `product`.

<div id="quantized-codecs-settings">
  #### Paramètres
</div>

* `allow_experimental_codecs` — doit être activé pour déclarer un codec `Quantized` (par défaut : `0`).
* `vector_search_use_quantized_codes` — active la réécriture en deux étapes avec présélection puis réévaluation du score (par défaut : `0`). Lorsqu’elle est désactivée, les requêtes correspondantes parcourent exactement les vecteurs en pleine précision.
* `vector_search_index_fetch_multiplier` — nombre de candidats à présélectionner par rapport au `LIMIT` de la requête : le parcours conserve les `LIMIT × multiplier` meilleurs codes avant la réévaluation du score. Des valeurs plus élevées améliorent le rappel au prix d’une réévaluation plus importante. La valeur par défaut est `1` (pas de suréchantillonnage) ; il faut donc généralement l’augmenter — par exemple à `10` ou plus — pour obtenir un bon rappel.

<div id="quantized-codecs-built-for-scale">
  #### Conçu pour la montée en charge
</div>

Le codec convient bien à ClickHouse, car la partie coûteuse — le parcours — est précisément ce que le moteur ClickHouse sait faire efficacement :

* **Vectorisé.** Les noyaux de parcours sont écrits pour SIMD, avec une sélection à l’exécution des instructions les plus larges prises en charge par le CPU : un `popcount` matériel pour les méthodes à code de signe (`rabitq`, `turboquant`) et des fused multiply-add larges pour les autres.
* **Parallèle sur les cœurs et les parts.** Un parcours linéaire est trivialement parallélisable, et ClickHouse le traite comme tel : les distances sont calculées sur tous les threads disponibles et sur toutes les parts d’une table à la fois, seule la fusion finale du top-`k` étant sérialisée.
* **Distribué.** Sur un cluster shardé, le travail se répartit entre les machines — chaque shard parcourt sa propre portion en parallèle et le coordinateur fusionne les shortlists.
* **Orienté colonnes et compatible avec les filtres.** Les codes quantifiés occupent leur propre colonne, sont compressés et lus via le même chemin d’E/S que toutes les autres colonnes, de sorte qu’un `WHERE` sélectif laisse simplement moins de codes à parcourir.
* **Aucune étape de construction distincte.** Les codes sont produits à mesure que les vecteurs sont écrits et fusionnent par concaténation — il n’y a aucun index à construire, à régler ou à reconstruire, si bien qu’une table est prête pour la recherche dès que ses données arrivent.

Les codes servent uniquement à générer des candidats ; la colonne en pleine précision, conservée en place, fournit le classement final exact.

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

Une approche courante pour accélérer la recherche vectorielle exacte consiste à utiliser un [type de données flottant](/docs/fr/reference/data-types/float) de plus faible précision.
Par exemple, si les vecteurs sont stockés sous la forme `Array(BFloat16)` au lieu de `Array(Float32)`, la taille des données est réduite de moitié, et le temps d’exécution des requêtes devrait diminuer dans les mêmes proportions.
Cette méthode est appelée quantification. Bien qu’elle accélère les calculs, elle peut réduire la précision des résultats malgré un balayage exhaustif de tous les vecteurs.

Avec la quantification traditionnelle, on perd en précision à la fois lors de la recherche et lors du stockage des données. Dans l’exemple ci-dessus, on stockerait `BFloat16` au lieu de `Float32`, ce qui signifie qu’il ne serait ensuite plus possible d’effectuer une recherche plus précise, même si on le souhaitait. Une autre approche consiste à stocker deux copies des données : une quantifiée et une en pleine précision. Bien que cela fonctionne, cela nécessite un stockage redondant. Prenons un scénario où `Float64` est le format de données d’origine et où l’on souhaite exécuter des recherches avec différents niveaux de précision (16 bits, 32 bits ou 64 bits complets). Il faudrait alors stocker trois copies distinctes des données.

ClickHouse propose le type de données Quantized Bit (`QBit`), qui répond à ces limites en :

1. Stockant les données d’origine en pleine précision.
2. Permettant de spécifier la précision de quantification au moment de la requête.

Cela est rendu possible en stockant les données dans un format groupé par bits (c’est-à-dire que tous les i-ièmes bits de tous les vecteurs sont stockés ensemble), ce qui permet de ne lire que le niveau de précision demandé. Vous bénéficiez ainsi des gains de vitesse liés à la réduction des E/S et des calculs apportée par la quantification, tout en conservant l’intégralité des données d’origine lorsque nécessaire. Lorsque la précision maximale est sélectionnée, la recherche devient exacte.

Pour déclarer une colonne de type `QBit`, utilisez la syntaxe suivante :

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

Où :

* `element_type` – le type de chaque élément du vecteur. Les types pris en charge sont `Int8`, `BFloat16`, `Float32` et `Float64`
* `dimension` – le nombre d’éléments de chaque vecteur
* `stride` – facultatif. Un diviseur de `dimension` qui partitionne les dimensions en `dimension / stride` groupes contigus stockés dans des flux distincts, de sorte qu’une recherche portant uniquement sur les premières dimensions lise moins de flux (utile pour les embeddings Matryoshka). La valeur par défaut est `dimension`, auquel cas le type est identique, octet pour octet, à un `QBit` sans stride. Voir la [page du type de données `QBit`](/docs/fr/reference/data-types/qbit) pour plus de détails.

<div id="qbit-create">
  #### Création d’une table `QBit` et ajout de données
</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">
  #### Recherche vectorielle avec `QBit`
</div>

Cherchons les plus proches voisins d’un vecteur représentant le mot 'lemon' à l’aide de la distance L2. Le troisième paramètre de la fonction de distance indique la précision en bits : des valeurs plus élevées offrent une meilleure précision, mais nécessitent davantage de calculs.

Vous trouverez [ici](/docs/fr/reference/data-types/qbit#vector-search-functions) toutes les fonctions de distance disponibles pour `QBit`.

**Recherche à pleine précision (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 │
   └────────┴─────────────────────┘
```

**Recherche en précision réduite :**

```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 │
   └────────┴────────────────────┘
```

Notez qu’avec une quantification sur 12 bits, on obtient une bonne approximation des distances et une exécution plus rapide des requêtes. L’ordre relatif reste globalement le même, 'apple' demeurant toujours la correspondance la plus proche.

<div id="qbit-performance">
  #### Considérations relatives aux performances
</div>

Le gain de performances apporté par `QBit` vient de la réduction des opérations d’E/S, car moins de données doivent être lues depuis le stockage lorsqu’on utilise une précision plus faible. De plus, lorsque `QBit` contient des données `Float32`, si le paramètre de précision est inférieur ou égal à 16, la réduction des calculs apporte aussi des gains supplémentaires. Le paramètre de précision contrôle directement le compromis entre précision et vitesse :

* **Précision plus élevée** (plus proche de la largeur des données d’origine) : résultats plus précis, requêtes plus lentes
* **Précision plus faible** : requêtes plus rapides avec des résultats approximatifs, utilisation de la mémoire réduite

<div id="references">
  ### Références
</div>

Articles de blog :

* [Recherche vectorielle avec ClickHouse - Partie 1](https://clickhouse.com/blog/vector-search-clickhouse-p1)
* [Recherche vectorielle avec ClickHouse - Partie 2](https://clickhouse.com/blog/vector-search-clickhouse-p2)
* [Nous avons conçu un moteur de recherche vectorielle qui vous permet de choisir la précision à l’exécution de la requête](https://clickhouse.com/blog/qbit-vector-search)
