Intermédiaire 11 minStack

pgvector : la recherche vectorielle dans PostgreSQL

Réponse directe

pgvector est une extension open source de PostgreSQL (licence PostgreSQL, permissive) qui ajoute le stockage et la recherche de vecteurs à une base que vous administrez déjà, avec des colonnes indexables jusqu'à 2 000 dimensions (4 000 en demi-précision). Pour un corpus local de quelques centaines de milliers de morceaux, c'est un service de moins à exploiter et des filtres SQL qui fonctionnent, sans synchronisation à maintenir avec vos données relationnelles.

pgvector est une extension de PostgreSQL qui ajoute le stockage et la recherche de vecteurs à une base que vous administrez déjà. Pour la plupart des projets de recherche documentaire locale, c'est un service de moins à faire tourner, une sauvegarde de moins à organiser, et des filtres SQL qui fonctionnent réellement — y compris sur les droits d'accès. Le projet, maintenu sous licence PostgreSQL et hébergé sur GitHub, comptait plus de 23 000 étoiles fin septembre 2026, à la version 0.8.6, compatible PostgreSQL 13 et plus récent.

Par Mohamed Meguedmi·Màj 2026-09-28·Testé sur Windows, macOS, Linux

#L'argument : ne pas ajouter de service

Une installation documentaire locale fait déjà tourner un serveur de modèle, une étape d'encodage et un stockage de documents. Ajouter une base vectorielle dédiée, c'est un conteneur de plus, un port de plus, une sauvegarde de plus, et une chose de plus qui peut se désynchroniser de vos données relationnelles le jour où un document est supprimé.

Si PostgreSQL est déjà là — et dans une application métier, il l'est presque toujours — pgvector supprime toute cette catégorie de problèmes. Vos morceaux de texte vivent dans une table à côté des documents dont ils viennent, avec des clés étrangères qui les maintiennent cohérents, et une suppression se propage comme vous l'attendez, sans tâche de nettoyage séparée à écrire ni à surveiller. Le projet ajoute au passage la recherche exacte et approximative, les vecteurs simple précision, demi-précision, binaires et creux, cinq distances (L2, produit scalaire, cosinus, L1, Hamming, Jaccard), et hérite gratuitement de la conformité ACID, de la récupération à un instant donné et des jointures de PostgreSQL.

#Comment ça marche en pratique

Le kit RAG Local

Tes documents, ton IA : un RAG local fiable sur tes PDF, notes et mails — sans rien envoyer dans le cloud.

  • Espace en ligne à vie
  • PDF + fichiers
  • Mises à jour à vie

Vous stockez une colonne de vecteurs à côté de vos colonnes habituelles. Une requête trie les lignes par distance au vecteur de la question et renvoie les plus proches. Trois opérateurs de distance couvrent les cas courants, et celui que vous choisissez doit correspondre à la convention du modèle d'embedding : c'est la source la plus fréquente de résultats discrètement médiocres. Pour des vecteurs normalisés à longueur 1 (le cas d'OpenAI et de la plupart des modèles d'embedding récents), le produit scalaire est le plus rapide à calculer et donne le même classement que le cosinus.

Les pièces du puzzle
ÉlémentCe que c'estÀ surveiller
Colonne vecteurUn tableau de flottants de dimension fixeLa dimension vient du modèle d'embedding et ne change pas sans tout réencoder
Opérateur de distanceCosinus, produit scalaire ou euclidien (L2)Doit correspondre au modèle
Index HNSWIndex en graphe, requêtes rapidesConstruction lente et gourmande en mémoire ; le défaut raisonnable depuis la version 0.5
Index IVFFlatIndex par partitions, peu coûteux à construireÀ créer une fois des données représentatives présentes
Aucun indexParcours exact de toutes les lignesParfaitement viable jusqu'à quelques dizaines de milliers de lignes

#Le SQL minimal pour démarrer

  1. 01
    Activer l'extension
    CREATE EXTENSION IF NOT EXISTS vector; — une seule commande, à exécuter une fois par base.
  2. 02
    Ajouter la colonne
    ALTER TABLE chunks ADD COLUMN embedding vector(1024); — la dimension doit correspondre exactement à celle du modèle d'embedding utilisé.
  3. 03
    Charger les données avant d'indexer
    Un chargement en masse via COPY est plus rapide sans index existant ; créez l'index une fois une quantité représentative de lignes présente.
  4. 04
    Créer l'index
    CREATE INDEX CONCURRENTLY ON chunks USING hnsw (embedding vector_cosine_ops); — l'option CONCURRENTLY évite de bloquer les écritures pendant la construction, longue sur une grande table.
  5. 05
    Interroger
    SELECT contenu FROM chunks WHERE document_id IN (SELECT id FROM documents WHERE utilisateur_autorise($1)) ORDER BY embedding <=> $2 LIMIT 5; — le filtre d'habilitation et le tri par similarité dans la même requête.

#Les limites de dimension, concrètement

pgvector définit quatre types de colonnes, chacun avec sa propre limite de dimension indexable. Le type vector standard (simple précision, 4 octets par élément) s'indexe jusqu'à 2 000 dimensions, ce qui couvre la plupart des modèles d'embedding open source courants (384 à 1 024 dimensions). Un modèle plus large comme text-embedding-3-large d'OpenAI, à 3 072 dimensions par défaut, dépasse cette limite : la parade documentée est soit de réduire la dimension à la génération de l'embedding (l'API le permet), soit de passer au type halfvec, qui stocke en demi-précision (2 octets par élément, moitié moins de place) et s'indexe jusqu'à 4 000 dimensions. Le type bit (vecteurs binaires, distances de Hamming ou Jaccard) monte à 64 000 dimensions indexables, et sparsevec (vecteurs creux) à 1 000 éléments non nuls indexés — au-delà, PostgreSQL stocke toujours la colonne (jusqu'à 16 000 dimensions pour vector, halfvec et sparsevec) mais sans pouvoir l'indexer, ce qui ramène à un parcours exact.

→
La quantification n'est pas qu'un détail de stockage
Passer de vector à halfvec réduit le poids de chaque ligne de moitié, ce qui tient davantage d'index en mémoire et accélère les requêtes à grande échelle sans changer votre modèle d'embedding. La quantification binaire (type bit) va plus loin : elle compresse l'index pour la recherche, puis un ré-ordonnancement sur les vecteurs complets restaure la précision perdue — la méthode documentée par le projet lui-même pour garder un index volumineux entièrement en mémoire plutôt que de le laisser déborder sur disque.

#Le filtrage, et les droits d'accès

Une question réelle porte rarement sur la totalité du corpus : on veut les passages de ce service, postérieurs à cette date, dans les documents que cet utilisateur a le droit de lire. Dans une base vectorielle dédiée, c'est un filtre de métadonnées avec sa syntaxe et ses cas limites. Dans PostgreSQL, c'est une clause WHERE à côté d'un tri par similarité, jointe à votre table d'utilisateurs si nécessaire.

Un piège documenté par le projet lui-même mérite d'être connu avant de le découvrir en production : avec un index approximatif (HNSW ou IVFFlat), le filtre s'applique après le parcours de l'index, pas avant. Si une condition ne retient que 10 % des lignes et que le paramètre par défaut hnsw.ef_search vaut 40, seules 4 lignes en moyenne remontent — pas les dix demandées. La parade officielle, disponible depuis la version 0.8.0, s'appelle le parcours d'index itératif (SET hnsw.iterative_scan = strict_order), qui relance automatiquement le parcours jusqu'à trouver assez de résultats plutôt que de renvoyer un ensemble tronqué en silence.

!
Les droits ne se règlent pas dans le prompt
Quand plusieurs personnes interrogent un même index, le filtre sur les habilitations est ce qui empêche un modèle de citer à quelqu'un un document qu'il ne devrait pas voir. Aucune consigne de prompt ne remplace ce filtre, et l'exprimer en SQL contre votre modèle d'autorisation existant est bien plus sûr que de le réimplémenter.

#Là où ça s'arrête

La mémoire à grande échelle
Des millions de vecteurs en pleine précision, c'est volumineux ; halfvec et la quantification binaire réduisent l'empreinte, mais les moteurs dédiés poussent la compression plus loin nativement. C'est l'écart le plus net.
Le temps de construction d'index
Un index HNSW sur une très grande table est long et gourmand ; en production, le construire avec CREATE INDEX CONCURRENTLY évite de bloquer les écritures pendant l'opération.
La concurrence
De la recherche vectorielle lourde à côté de votre charge transactionnelle met les deux sur le même serveur. Des réplicas en lecture aident, séparer les rôles aide davantage.
La dimension des vecteurs
Les vecteurs indexés ont une borne selon leur type (2 000 pour vector, 4 000 pour halfvec). Les modèles courants passent ; un modèle à très grande dimension doit être réduit ou quantifié.
La recherche hybride
PostgreSQL sait faire de la recherche plein texte (tsvector) et vous pouvez la combiner à la distance vectorielle, mais l'ergonomie est à votre charge : il faut exécuter les deux requêtes séparément puis fusionner les classements, par exemple avec une fusion de rangs réciproques (Reciprocal Rank Fusion), plutôt qu'obtenir un score hybride natif en une seule requête.
La montée en charge horizontale
Au-delà d'un seul serveur, la voie documentée passe par des réplicas PostgreSQL en lecture ou par un outil de répartition comme Citus ou PgDog — une brique en plus, à l'inverse de l'argument de départ.
i
Le VACUUM sur un index HNSW peut être lent
La documentation officielle le signale explicitement : le nettoyage (VACUUM) d'une table dont l'index vectoriel est en HNSW peut prendre du temps sur un gros volume. L'accélérer passe par un REINDEX INDEX CONCURRENTLY avant le VACUUM plutôt que de laisser l'opération de maintenance tourner seule — un détail d'exploitation que peu de tutoriels mentionnent avant que la maintenance nocturne ne dépasse sa fenêtre.

#pgvector ou une base dédiée

La question n'est pas laquelle est objectivement meilleure, mais laquelle correspond à votre situation aujourd'hui. pgvector gagne dès que PostgreSQL est déjà la base de vérité de l'application : facturation, comptes, documents source. Un moteur dédié gagne quand le volume de vecteurs ou le débit de requêtes dépasse ce qu'un seul serveur transactionnel peut absorber sans dégrader le reste de l'application, ou quand l'équipe préfère isoler le composant IA du reste du système d'information pour des raisons d'exploitation plutôt que de performance pure.

Choisir selon la situation
SituationChoix
PostgreSQL déjà en place, moins de quelques centaines de milliers de morceauxpgvector, confortablement
Les vecteurs doivent rester cohérents avec des données relationnellespgvector : les transactions le font gratuitement
Filtrage fin sur des habilitations existantespgvector
Modèle d'embedding à plus de 4 000 dimensions sans réduction possibleVérifier le type sparsevec, ou une base dédiée pensée pour ce cas
Des millions de vecteurs, beaucoup de requêtesUn moteur dédié (Qdrant, Milvus)
Prototype dans un carnet de notesN'importe lequel, ce choix est réversible

#FAQ

pgvector est-il assez rapide pour du RAG ?+
Pour un corpus local typique — de quelques dizaines à quelques centaines de milliers de morceaux — oui, avec un index HNSW, et souvent même sans index en bas de cette fourchette. La récupération est rarement l'étape lente d'une chaîne locale : c'est la génération par le modèle qui domine le temps de réponse perçu.
pgvector ou Qdrant ?+
pgvector si PostgreSQL est déjà là et que vos données sont relationnelles : moins de services, cohérence transactionnelle, filtres SQL natifs, et une seule sauvegarde à organiser. Qdrant quand l'échelle, la quantification mémoire agressive ou un service dédié entièrement pensé pour le vectoriel comptent davantage que la simplicité d'exploitation d'une base unique. Les deux répondent honnêtement au même besoin jusqu'à plusieurs centaines de milliers de vecteurs.
Quel index choisir, HNSW ou IVFFlat ?+
HNSW par défaut depuis plusieurs versions : meilleures performances en requête, au prix d'une construction plus lente et de davantage de mémoire. IVFFlat est moins coûteux à construire mais doit être créé une fois des données représentatives déjà chargées, sous peine de partitions mal réparties.
Puis-je filtrer par métadonnées ?+
Oui, en SQL ordinaire, jointures comprises. C'est l'une des meilleures raisons de le choisir, en particulier pour filtrer par droits d'accès. Attention toutefois : avec un index approximatif, ce filtre s'applique après le parcours de l'index, ce qui peut renvoyer moins de résultats que prévu sans le parcours itératif.
Que se passe-t-il si je change de modèle d'embedding ?+
Tout doit être réencodé : la dimension et la géométrie des vecteurs appartiennent au modèle, pas à la base. C'est un traitement par lots sur GPU, pas une migration de schéma, et il faut recréer la colonne si la nouvelle dimension dépasse celle déclarée. Prévoyez une fenêtre de bascule, car l'ancien index reste valide tant que le réencodage n'est pas terminé.
Mon modèle d'embedding fait plus de 2 000 dimensions, que faire ?+
Le type vector standard indexe jusqu'à 2 000 dimensions. Au-delà, passez au type halfvec (jusqu'à 4 000, en demi-précision) ou réduisez la dimension à la génération si votre fournisseur le permet, comme OpenAI le propose pour text-embedding-3-large. Sans index, PostgreSQL stocke tout de même jusqu'à 16 000 dimensions, mais en parcours exact.
Comment diagnostiquer une requête vectorielle lente ?+
La documentation recommande EXPLAIN (ANALYZE, BUFFERS) devant la requête pour voir si l'index est réellement utilisé et combien de blocs sont lus. Une requête d'exactitude sans index gagne à augmenter max_parallel_workers_per_gather ; une requête approximative lente pointe souvent vers un index encore en construction ou de la mémoire insuffisante pour le garder en cache.
Ce guide vous a aidé ?

Un retour, une erreur, une précision ? Faites-nous signe, ça améliore le guide pour tout le monde.