Retour au blog
Guide Technique · 9 min de lecture

Index HNSW dans Postgres : bien indexer pour la recherche vectorielle

Affane Daylami · Fondateur· 24 août 2026

HNSW est l’algorithme d’indexation que pgvector recommande pour la recherche vectorielle par similarité sur Postgres. Ce guide montre comment créer un index HNSW correctement réglé. Trois choix comptent : le type de colonne selon la dimension de vos embeddings, les paramètres m et ef_construction à la construction, et ef_search à chaque requête pour arbitrer recall et latence.

La recherche vectorielle native d’Aurabase (RAG, pgvector, embeddings) s’appuie sur ce même mécanisme d’indexation, décrit en détail sur la page IA native sur Postgres. Ce guide suppose une table Postgres avec pgvector déjà installé, une colonne de type vector, et au moins quelques milliers de lignes. En dessous, un simple parcours séquentiel reste souvent plus rapide qu’un index approximatif.

L’essentiel
  • HNSW ne demande aucune phase d’entraînement, contrairement à IVFFlat : l’index se construit au fil des insertions, disponible dans pgvector depuis la version 0.5.0.
  • Deux paramètres fixent la qualité de l’index à la construction : m (connexions par nœud, défaut 16) et ef_construction (largeur de recherche à la construction, défaut 64).
  • Un troisième paramètre, hnsw.ef_search (défaut pgvector : 40), se règle à chaque requête, sans reconstruire l’index, pour arbitrer recall et latence.
  • pgvector plafonne l’indexation HNSW du type vector à 2000 dimensions. Au-delà (un embedding à 3072 dimensions, par exemple), un cast vers halfvec est nécessaire pour indexer.
  • pgvector 0.8.6 est la version embarquée dans l’image Postgres tenant d’Aurabase, vérifiée directement dans le Dockerfile le 24 août 2026.
#
Comprendre

Qu’est-ce qu’un index HNSW dans pgvector ?

HNSW signifie Hierarchical Navigable Small World. C’est un index de graphe : chaque vecteur devient un nœud relié à ses voisins les plus proches, organisés en plusieurs couches superposées. Une recherche démarre au sommet du graphe, sur la couche la plus clairsemée, puis descend couche par couche vers les voisins les plus pertinents. Le temps de recherche devient ainsi quasi logarithmique, pas linéaire sur le nombre de lignes.

IVFFlat, l’autre index de pgvector, fonctionne différemment : il découpe l’espace vectoriel en listes déterminées par un passage d’entraînement sur un échantillon existant, avant de pouvoir indexer quoi que ce soit. HNSW n’a pas cette contrainte, chaque insertion enrichit directement le graphe, ce qui le rend plus simple à opérer sur une table qui grossit en continu. En contrepartie, un index HNSW consomme davantage de mémoire et prend plus de temps à construire qu’un IVFFlat équivalent sur le même volume.

pgvector introduit le support HNSW en version 0.5.0. Les versions ultérieures ajoutent des capacités utiles pour ce guide : le type halfvec (0.7.0) pour indexer au-delà de 2000 dimensions, et le paramètre hnsw.iterative_scan (0.8.0) pour améliorer le recall sur des requêtes filtrées. Si vous comparez pgvector à une base vectorielle dédiée avant de trancher, notre comparatif pgvector vs Pinecone, Weaviate et Qdrant détaille les compromis.

#
Étape 1

Vérifiez votre version de pgvector avant de créer l’index

Confirmez la version de pgvector installée avant toute chose. Une extension trop ancienne fait échouer silencieusement certaines fonctionnalités de ce guide, en particulier halfvec et hnsw.iterative_scan.

psql
SQL
SELECT extversion FROM pg_extension WHERE extname = 'vector';

HNSW existe depuis pgvector 0.5.0. Le type halfvec, nécessaire pour indexer des embeddings au-delà de 2000 dimensions, demande au moins la version 0.7.0. Le paramètre hnsw.iterative_scan demande la version 0.8.0.

Sur les projets Aurabase, la question ne se pose pas : l’image Postgres tenant embarque pgvector 0.8.6, aussi bien sur le cluster Postgres partagé (docker/Postgres.Dockerfile, construit directement sur pgvector/pgvector:0.8.6-pg16-bookworm) que sur les instances Postgres 16 CNPG dédiées par projet (docker/Postgres.CNPG.Dockerfile, qui hérite pgvector 0.8.6 de l’image CloudNativePG officielle). Vérifié dans les deux Dockerfiles le 24 août 2026.

#
Étape 2

Choisissez le bon type de colonne selon la dimension de vos embeddings

Le type de colonne dépend de la dimension de vos embeddings, pas seulement du modèle qui les génère. pgvector stocke un vecteur classique dans le type vector, avec un plafond de 16 000 dimensions en stockage. Mais l’indexation HNSW sur ce type, elle, est limitée à 2000 dimensions : au-delà, CREATE INDEX échoue.

Les modèles d’embeddings courants dépassent souvent ce seuil : text-embedding-3-large d’OpenAI ou gemini-embedding-2 de Google produisent nativement jusqu’à 3072 dimensions. Pour indexer ces vecteurs avec HNSW, castez la colonne vers halfvec (précision de stockage réduite de moitié), ce qui repousse la limite d’indexation bien au-delà de 2000 dimensions.

DimensionColonneHNSW sur vectorNécessite un cast
768embedding_768OuiNon
1536embedding_1536OuiNon
3072embedding_3072Non (> 2000 dims)Oui, cast ::halfvec(3072)

Le moteur RAG d’Aurabase illustre ce compromis en production : trois classes de dimensions supportées (768, 1536, 3072), stockées dans trois colonnes distinctes de la même table embeddings. Les colonnes 768 et 1536 sont indexées directement en HNSW sur le type vector. La colonne 3072 est indexée via un cast ::halfvec(3072), précisément pour contourner le plafond des 2000 dimensions.

migration.sql
SQL
CREATE INDEX idx_embeddings_vec_768 ON embeddings
USING hnsw (embedding_768 vector_cosine_ops)
WHERE embedding_768 IS NOT NULL;
CREATE INDEX idx_embeddings_vec_1536 ON embeddings
USING hnsw (embedding_1536 vector_cosine_ops)
WHERE embedding_1536 IS NOT NULL;
CREATE INDEX idx_embeddings_vec_3072 ON embeddings
USING hnsw ((embedding_3072::halfvec(3072)) halfvec_cosine_ops)
WHERE embedding_3072 IS NOT NULL;

Pour le détail de l’ingestion (chunking, appel au fournisseur d’embeddings, insertion), voir le tutoriel pipeline RAG sur pgvector.

#
Étape 3

Créez l’index avec les paramètres m et ef_construction

La syntaxe minimale suffit pour un premier index, avec les valeurs par défaut de pgvector.

psql
SQL
CREATE INDEX ON items
USING hnsw (embedding vector_cosine_ops);

pgvector applique alors m = 16 et ef_construction = 64. Pour ajuster ces valeurs explicitement, utilisez la clause WITH :

psql
SQL
CREATE INDEX ON items
USING hnsw (embedding vector_cosine_ops)
WITH (m = 24, ef_construction = 100);
Accélérer une grosse construction
Avant de construire un index HNSW sur une table volumineuse, augmentez temporairement maintenance_work_mem pour la session : c’est, selon la documentation pgvector elle-même, le levier le plus direct pour réduire le temps de construction.

Que change le paramètre m ?

m fixe le nombre maximal de connexions que chaque nœud du graphe conserve par couche. Une valeur plus élevée densifie le graphe : le recall augmente, mais la mémoire consommée et le temps de construction augmentent aussi, à peu près linéairement. La valeur par défaut (16) convient à la plupart des cas. Monter à 24 ou 32 se justifie surtout sur des embeddings de grande dimension, où la distinction entre voisins proches et lointains devient plus fine.

Que change ef_construction ?

ef_construction fixe la taille de la liste de candidats explorée pendant la construction de l’index, pour chaque nœud inséré. Une valeur plus élevée améliore la qualité du graphe final, donc le recall potentiel, au prix d’un temps de construction plus long. Contrairement à m, ce paramètre n’a aucun coût au moment de la requête : c’est un investissement ponctuel, payé une seule fois à la création de l’index.

Index partiels pour plusieurs classes de dimensions dans une même table

Quand une table stocke plusieurs colonnes vectorielles (une par classe de dimension, comme le fait Aurabase), indexez chaque colonne séparément avec une clause WHERE colonne IS NOT NULL. Cet index partiel évite d’indexer des lignes vides pour les classes non utilisées par une ligne donnée, ce qui réduit la taille de l’index et accélère sa construction sans rien coûter en recall.

Le choix de la classe d’opérateurs (vector_cosine_ops, vector_l2_ops ou vector_ip_ops) doit correspondre à la métrique sur laquelle le modèle d’embedding a été entraîné. La plupart des modèles de text embeddings récents sont entraînés pour la similarité cosinus : vector_cosine_ops (ou halfvec_cosine_ops sur une colonne castée) est donc le choix par défaut le plus sûr.

#
Étape 4

Réglez ef_search au moment de la requête

ef_search se règle à chaque requête, pas à la construction de l’index. Il fixe la taille de la liste de candidats explorée pendant la recherche : plus il est élevé, meilleur est le recall, au prix d’une latence plus longue. pgvector fixe sa valeur par défaut à 40.

psql
SQL
SET LOCAL hnsw.ef_search = 100;
SELECT id, content
FROM embeddings
ORDER BY embedding <=> '[...]'::vector
LIMIT 10;

40 suffit rarement dès qu’une requête combine la recherche vectorielle avec un filtre WHERE appliqué après le parcours de l’index (sur un namespace, un tenant, ou tout autre critère de métadonnées). Le parcours HNSW ramène ef_search candidats bruts, puis le filtre en écarte une partie. Si trop peu de candidats survivent, le LIMIT final se retrouve sous-rempli.

Le moteur RAG d’Aurabase élargit donc ef_search dynamiquement selon le top_k demandé, au lieu de garder la valeur fixe de 40 : ef = max(top_k × 4, 64). Une recherche des 5 résultats les plus proches utilise ef_search = 64 ; une recherche des 50 meilleurs utilise ef_search = 200. Cette formule reste ajustable par variable d’environnement pour les déploiements qui ont besoin d’un autre compromis recall/latence.

pgvector 0.8 ajoute un second levier pour ce même problème : hnsw.iterative_scan. En mode strict_order ou relaxed_order, le parcours élargit progressivement sa recherche jusqu’à réunir assez de résultats après filtre, plutôt que de s’arrêter sur une liste de candidats figée. Aurabase l’active par défaut en strict_order, mais protège l’appel dans un savepoint. Sur une version de pgvector antérieure à 0.8, où ce paramètre n’existe pas, la requête continue en mode dégradé plutôt que d’échouer.

#
Aller plus loin

Construire le pipeline RAG complet

Cet index HNSW n’est qu’une pièce du pipeline RAG complet : chunking, génération des embeddings, ingestion, puis recherche. Notre tutoriel pas-à-pas construit ce pipeline de bout en bout sur pgvector, de la première insertion jusqu’à la requête de similarité. La documentation technique détaille par ailleurs l’ensemble des capacités IA natives d’Aurabase construites sur Postgres.

#
Questions Fréquentes

FAQ

HNSW ou IVFFlat : lequel choisir avec pgvector ?+
HNSW convient à la grande majorité des cas de recherche vectorielle en production : meilleur recall à latence égale, aucune phase d’entraînement préalable, et une bonne tolérance aux tables qui grossissent en continu. IVFFlat reste pertinent quand la mémoire disponible est très contrainte, au prix d’un recall généralement inférieur et d’un ré-entraînement nécessaire si la distribution des données change fortement.
Combien de mémoire prévoir pour un index HNSW ?+
L’ordre de grandeur dépend directement de m et du nombre de vecteurs indexés : chaque nœud stocke jusqu’à m connexions par couche, en plus du vecteur lui-même. Pour une estimation fiable sur votre volumétrie réelle, construisez l’index sur un sous-ensemble représentatif de vos données. Mesurez ensuite sa taille avec pg_relation_size() plutôt que de vous fier à une règle générale non mesurée.
Peut-on indexer des embeddings de plus de 2000 dimensions avec HNSW ?+
Pas directement sur le type vector : pgvector refuse de construire un index HNSW au-delà de 2000 dimensions sur ce type. La solution consiste à caster la colonne vers halfvec au moment de la création de l’index, ce qui repousse la limite d’indexation en réduisant de moitié la précision de stockage. C’est exactement l’approche utilisée en production sur la classe d’embeddings à 3072 dimensions d’Aurabase.
ESSAYEZ MAINTENANT

Indexez vos embeddings en quelques minutes.

Un projet Aurabase suffit : pgvector 0.8.6 et l’extension HNSW sont déjà prêts, sur base partagée comme sur base dédiée.

Créer un projet Documentation RAG & pgvector
Aucune carte bancaire requise · 500 MB gratuits · 50 000 MAU