Aller au contenu
Cercle IA Tech · Embeddings Un site du réseau Cercle IA Tech

Guide

pgvector : index HNSW ou IVFFlat, création et réglages

Créer un index HNSW ou IVFFlat dans pgvector : syntaxe, paramètres m, ef_construction, ef_search, lists, probes, et comment vérifier le rappel.

Mis à jour le

Pour la plupart des projets, choisissez HNSW : la documentation de pgvector le donne meilleur en compromis vitesse-rappel, et il se crée même sur une table vide. IVFFlat se construit plus vite et prend moins de mémoire, mais interroge moins bien, et doit être créé après le chargement des données. Les commandes et valeurs ci-dessous viennent du dépôt pgvector, lu le 15/11/2026.

Les deux index en un tableau

HNSW IVFFlat
Principe graphe à plusieurs niveaux vecteurs répartis en listes, on ne fouille que certaines listes
Qualité à l'interrogation meilleure (compromis vitesse-rappel) moins bonne
Construction plus lente, plus de mémoire plus rapide, moins de mémoire
Table vide possible (pas d'étape d'entraînement) à éviter : créer après les données
Réglage à la création m, ef_construction lists
Réglage à la requête hnsw.ef_search ivfflat.probes

Le fonctionnement du graphe HNSW est expliqué sur la page Index HNSW.

Créer un index HNSW

Un index par fonction de distance utilisée. Pour la distance cosinus :

CREATE INDEX ON passages USING hnsw (embedding vector_cosine_ops);

Les classes d'opérateurs : vector_l2_ops (distance euclidienne), vector_ip_ops (produit scalaire), vector_cosine_ops (cosinus), vector_l1_ops, et bit_hamming_ops, bit_jaccard_ops pour les vecteurs binaires. Pour halfvec, les équivalents s'appellent halfvec_l2_ops, halfvec_cosine_ops, etc.

Paramètres de construction

CREATE INDEX ON passages USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
  • m : nombre maximal de connexions par niveau du graphe ; 16 par défaut.
  • ef_construction : taille de la liste dynamique de candidats pendant la construction du graphe ; 64 par défaut.

Paramètre de recherche

hnsw.ef_search fixe la taille de la liste de candidats pendant la recherche (40 par défaut). Plus haut : meilleur rappel, recherche plus lente. Pour une seule requête, dans une transaction :

BEGIN;
SET LOCAL hnsw.ef_search = 100;
SELECT id FROM passages ORDER BY embedding <=> $1 LIMIT 5;
COMMIT;

Créer un index IVFFlat

Les trois conseils de la documentation pour un bon rappel :

  1. créer l'index après que la table contient des données ;
  2. choisir le nombre de listes : lignes / 1000 jusqu'à un million de lignes, racine carrée(lignes) au-delà ;
  3. à l'interrogation, choisir le nombre de listes fouillées (probes) : plus haut pour le rappel, plus bas pour la vitesse ; racine carrée(lists) est un bon point de départ, la valeur par défaut est 1.
CREATE INDEX ON passages USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
SET ivfflat.probes = 10;

Exemple de calcul : pour 100 000 passages, 100 000 / 1 000 = 100 listes, et racine carrée de 100 = 10 listes fouillées.

Construire sans bloquer la production

  • Mémoire de construction. L'index se construit bien plus vite s'il tient dans maintenance_work_mem ; la documentation prévient de ne pas la régler au point d'épuiser la mémoire du serveur.
  • Parallélisme. max_parallel_maintenance_workers (2 par défaut) accélère la construction.
  • Sans bloquer les écritures. En production, créez l'index avec CREATE INDEX CONCURRENTLY.
  • Suivre l'avancement :
SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS "%"
FROM pg_stat_progress_create_index;

Les limites de dimension

Un index HNSW ou IVFFlat accepte au plus 2 000 dimensions pour vector, 4 000 pour halfvec, 64 000 pour bit. Avec un modèle à 3 072 dimensions, deux voies : réduire la dimension (voir Dimensions et Matryoshka) ou indexer une expression en demi-précision :

CREATE INDEX ON passages USING hnsw ((embedding::halfvec(3072)) halfvec_cosine_ops);

Comme dans l'exemple de la documentation, la requête trie alors sur la même expression (ORDER BY embedding::halfvec(3072) <=> …).

Filtres et index approximatif

Avec un filtre WHERE (par exemple sur les droits d'accès), l'index approximatif est parcouru d'abord, le filtre appliqué ensuite : la requête peut renvoyer moins de lignes que demandé. Depuis la version 0.8.0, les parcours itératifs corrigent cela :

SET hnsw.iterative_scan = strict_order;

Le dépôt conseille aussi de commencer par un index ordinaire sur la colonne filtrée, et, si le filtre ne prend que quelques valeurs distinctes, d'envisager des index partiels. Le sujet est développé sous l'angle des droits sur RAG et droits d'accès.

Vérifier le rappel

La documentation recommande de surveiller le rappel en comparant les résultats de la recherche approximative à ceux de la recherche exacte. Méthode simple : sur un échantillon de questions, lancez la requête avec l'index, puis sans (dans une transaction avec SET LOCAL enable_indexscan = off), et comptez la part des résultats exacts retrouvés. Ajustez ef_search ou probes jusqu'à un rappel qui vous convient.

Questions fréquentes

Pourquoi ma requête renvoie-t-elle d'autres résultats depuis que j'ai créé un index ?

Parce que HNSW et IVFFlat sont des index approximatifs : ils échangent un peu de rappel contre de la vitesse. La documentation de pgvector prévient que les résultats changent après l'ajout d'un tel index.

Mon index pgvector n'est pas utilisé, pourquoi ?

La documentation indique que le ORDER BY doit porter directement sur un opérateur de distance, en ordre croissant, et non sur une expression. Il faut aussi un index créé pour cette fonction de distance.

Sources

Pages consultées pour écrire cet article :

  1. https://github.com/pgvector/pgvector

À lire aussi