pgvector vs pgvectorscale vs VectorChord: Comparación de rendimiento para búsquedas vectoriales en PostgreSQL
La búsqueda vectorial convierte el texto en embeddings numéricos y encuentra documentos semánticamente similares usando métricas de proximidad. Palabras con significados similares se agrupan juntas en un espacio de N dimensiones. Un ejemplo clásico: Reina - Hombre + Mujer ≈ Reina ilustra analogías vectoriales.
Métricas clave
- Similitud coseno: ángulo entre vectores; los vectores normalizados dan resultados equivalentes a la distancia euclidiana.
- Distancia euclidiana: distancia en línea recta entre puntos.
Las extensiones de PostgreSQL implementan búsquedas ANN (vecino más cercano aproximado) usando IVFFlat, HNSW y algoritmos especializados.
Principales extensiones para búsquedas vectoriales
pgvector
Extensión de código abierto en C con el tipo vector. Soporta similitud coseno, distancia L2 y producto punto. Tipos de índice:
- IVFFlat: clustering de vectores.
- HNSW: grafo jerárquico, búsqueda gruesa hacia abajo.
Requiere cargar todo el índice en RAM.
pgvectorscale
Capa basada en Rust sobre pgvector (usando PGRX). Presenta el índice StreamingDiskANN (adaptación de DiskANN) que almacena datos en SSD, reduciendo el uso de RAM. Usa compresión SBQ (cuantificación binaria estadística).
Ganancia de rendimiento anunciada: 16x mayor throughput con 50 millones de vectores de dimensión 768.
pgvecto.rs
Escrita completamente en Rust, soporta formatos binarios, FP16 e INT8, con dimensiones hasta 65.535. El algoritmo VBASE está optimizado para consultas híbridas (vector + filtros + JOINs).
VectorChord
El sucesor activo de pgvecto.rs por TensorChord. Usa RaBitQ (cuantificación binaria aleatorizada) combinado con reordenamiento. Almacena 26 veces más datos al mismo costo.
Todas utilizan el tipo estándar vector, garantizando compatibilidad en migraciones.
Configuración del entorno de pruebas
Despliegue de tres bases de datos mediante 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
Inicialización:
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;
Makefile para orquestación:
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
Nombres de servicio y puertos únicos son obligatorios.
Ampliando el repositorio para operaciones masivas
El método bulk_create en 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
Esquema BulkCreateRequest:
class BulkCreateRequest(BaseModel):
documents: Annotated[
list[DocumentCreate],
Field(..., description='Lista de documentos a crear', min_length=1, max_length=500)
]
Endpoint:
@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
Preparación para pruebas de rendimiento
Dataset: ag_news de Hugging Face (titulares → embeddings, categoría → metadatos). Objetivo: 1 millón de registros.
Dependencias:
uv add --dev datasets ollama aiohttp tqdm
Componentes de prueba:
- Carga por lotes desde datasets
- Generación de embeddings mediante ollama
- Inserción masiva en base de datos
- Medición de latencia para consultas de búsqueda
La estructura de clase de prueba permite comparación paralela entre todas las extensiones.
Conclusiones clave
- Compatibilidad: Todas usan el tipo
vector; el código es portable. - Escalabilidad: pgvectorscale y VectorChord funcionan con almacenamiento en disco; pgvector requiere RAM.
- Compresión: RaBitQ (densidad 26x), SBQ para grandes conjuntos de datos.
- Búsqueda híbrida: VBASE en pgvecto.rs soporta filtros + JOINs.
- Rendimiento: StreamingDiskANN ofrece mejora de 16x en throughput con más de 50 millones de vectores.
— Editorial Team
Aún no hay comentarios.