pgvector vs pgvectorscale vs VectorChord : Comparatif des performances pour la recherche vectorielle dans PostgreSQL
La recherche vectorielle transforme le texte en embeddings numériques et trouve des documents sémantiquement similaires grâce à des métriques de proximité. Les mots ayant des sens proches se regroupent dans un espace à N dimensions. Un exemple classique : Roi - Homme + Femme ≈ Reine illustre les analogies vectorielles.
Métriques clés :
- Similarité cosinus : angle entre les vecteurs ; les vecteurs normalisés donnent des résultats équivalents à la distance euclidienne.
- Distance euclidienne : distance en ligne droite entre deux points.
Les extensions PostgreSQL implémentent la recherche ANN (Approximate Nearest Neighbor) via IVFFlat, HNSW et des algorithmes spécialisés.
Principales extensions de recherche vectorielle
pgvector
Extension open-source en C avec le type vector. Prend en charge la similarité cosinus, la distance L2 et le produit scalaire. Types d'index :
- IVFFlat : clustering de vecteurs.
- HNSW : graphe hiérarchique, recherche descendante par niveaux.
Nécessite un chargement complet de l'index en RAM.
pgvectorscale
Couche basée sur Rust au-dessus de pgvector (utilisant PGRX). Propose l'index StreamingDiskANN (adaptation de DiskANN) qui stocke les données sur SSD, réduisant ainsi l'utilisation de la RAM. Utilise la compression SBQ (Statistical Binary Quantization).
Gains de performance annoncés : 16x plus de débit sur 50 millions de vecteurs de dimension 768.
pgvecto.rs
Écrit entièrement en Rust, prend en charge les formats binaire, FP16 et INT8, avec des dimensions allant jusqu’à 65 535. L'algorithme VBASE est optimisé pour les requêtes hybrides (vecteurs + filtres + JOINs).
VectorChord
Le successeur actif de pgvecto.rs par TensorChord. Utilise RaBitQ (Randomized Binary Quantization) combiné à un réordonnancement. Stocke 26 fois plus de données au même coût.
Toutes utilisent le type standard vector, assurant une compatibilité totale lors des migrations.
Configuration de l'environnement de test
Déploiement de trois bases de données via Docker Compose :
- pgvectorscale (timescale/timescaledb-ha:pg18) :
services:
postgres:
image: timescale/timescaledb-ha:pg18
ports:
- "5432:5432"
volumes:
- pgdata:/home/postgres/pgdata
- ./db_init_pgv:/docker-entrypoint-initdb.d
Initialisation :
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS vectorscale;
- VectorChord (tensorchord/vchord-postgres:pg18-v1.1.1) :
CREATE EXTENSION IF NOT EXISTS vchord CASCADE;
Fichier Makefile pour l'orchestration :
COMPOSE := docker compose
PGV_FILE := docker-compose-pgv.yml
PGVS_FILE := docker-compose-pgvs.yml
VC_FILE := docker-compose-vc.yml
up-pgv:
$(COMPOSE) -f $(PGV_FILE) up --build -d
up-pgvs:
$(COMPOSE) -f $(PGVS_FILE) up --build -d
up-vc:
$(COMPOSE) -f $(VC_FILE) up --build -d
up: up-pgv up-pgvs up-vc
Les noms de services et les ports doivent être uniques.
Extension du dépôt pour les opérations en masse
La méthode bulk_create dans BaseRepository :
async def bulk_create(self, schemas: list[CreateSchemaType]) -> list[ModelType]:
instances = [
self.model(**self._normalize_embedding_format(schema.model_dump()))
for schema in schemas
]
self.session.add_all(instances)
await self.session.flush()
return instances
Schéma BulkCreateRequest :
class BulkCreateRequest(BaseModel):
documents: Annotated[
list[DocumentCreate],
Field(..., description='Liste des documents à créer', min_length=1, max_length=500)
]
Point d'entrée :
@router.post('/bulk', response_model=list[DocumentResponse], status_code=status.HTTP_201_CREATED)
async def bulk_create_documents(request: BulkCreateRequest):
async with transaction() as session:
repo = DocumentRepository(session)
created_docs = await repo.bulk_create(request.documents)
return created_docs
Préparation au benchmark
Jeux de données : ag_news depuis Hugging Face (titres → embeddings, catégorie → métadonnées). Objectif : 1 million d’enregistrements.
Dépendances :
uv add --dev datasets ollama aiohttp tqdm
Composants du test :
- Chargement par lots depuis les jeux de données
- Génération d’embeddings via ollama
- Insertion massive dans la base
- Mesure de latence pour les requêtes de recherche
La structure de classe de test permet une comparaison parallèle entre toutes les extensions.
Points clés
- Compatibilité : Toutes les extensions utilisent le type
vector; le code est portable. - Évolutivité : pgvectorscale et VectorChord fonctionnent avec le stockage disque ; pgvector nécessite de la RAM.
- Compression : RaBitQ (densité 26x), SBQ pour les grands jeux de données.
- Recherche hybride : VBASE dans pgvecto.rs supporte les filtres + JOINs.
- Performance : StreamingDiskANN offre une amélioration de 16x du débit sur 50 millions de vecteurs et plus.
— Editorial Team
Aucun commentaire pour le moment.