Bases de Datos Vectoriales Comparadas: Ejecutando el Mismo Pipeline RAG en pgvector, Qdrant, Pinecone y Weaviate
El Setup de Test
Dataset: 2.3M chunks de documentos de una base de conocimiento de soporte al cliente. Tamaño promedio de chunk: 512 tokens. Embeddeado con text-embedding-3-small (1536 dimensiones).
Queries: 10,000 queries reales de usuarios recolectadas de logs de producción (anonimizadas). Mix de cortas ("resetear contraseña"), medianas ("cómo configurar SSO con Azure AD"), y largas ("nuestro equipo está migrando de on-premise a cloud y necesitamos entender las implicaciones de residencia de datos para cumplimiento GDPR").
Hardware:
- pgvector: RDS
db.r7g.xlarge(4 vCPU, 32GB RAM), PostgreSQL 16 con pgvector 0.8 - Qdrant: cluster de 3 nodos en
c7g.xlarge(4 vCPU, 8GB RAM cada uno), Qdrant 1.13 - Pinecone: Serverless (Standard), us-east-1
- Weaviate: cluster de 3 nodos en
c7g.xlarge, Weaviate 1.28
Búsqueda: Top-10 vecinos más cercanos, similaridad coseno, con filtro de metadata (categoría = valor específico, reduciendo candidatos en ~80%).
Números Crudos: Latencia y Throughput
Latencia de Query (10K queries, ms)
| Base de datos | p50 | p95 | p99 | p99.9 |
|---|---|---|---|---|
| pgvector (HNSW) | 8.2 | 14.1 | 22.3 | 41.7 |
| pgvector (IVF) | 12.4 | 24.8 | 38.1 | 67.2 |
| Qdrant | 3.1 | 5.8 | 8.4 | 14.2 |
| Pinecone Serverless | 18.4 | 32.1 | 48.7 | 142.3 |
| Weaviate | 5.7 | 11.2 | 18.9 | 33.4 |
Qdrant gana en latencia cruda, y no es ni cerca. Su engine basado en Rust con almacenamiento memory-mapped es genuinamente rápido. Pero observar el p99.9 de Pinecone — ese es 142ms de cola son el cold start serverless afectando el rendimiento. Para aplicaciones interactivas, esa cola es un problema.
pgvector con HNSW es sorprendentemente competitivo. Con 8.2ms de p50, es suficientemente rápido para cualquier aplicación RAG. La brecha se amplía en la cola, pero para la mayoría de los case es de uso, es más que adecuado.
Throughput (queries/segundo, 100 conexiones concurrentes)
| Base de datos | QPS (sin filtro) | QPS (con filtro) | Delta |
|---|---|---|---|
| pgvector (HNSW) | 2,840 | 2,120 | -25.4% |
| Qdrant | 8,420 | 7,180 | -14.7% |
| Pinecone | 1,200 | 980 | -18.3% |
| Weaviate | 4,100 | 3,250 | -20.7% |
La diferencia de performance con filtrado es reveladora. pgvector pierde 25% de throughput con filtros porque los aplica post-búsqueda HNSW (una limitación conocida). Qdrant integra el filtrado en el algoritmo de búsqueda, así que la penalidad es menor. Weaviate cae en algún punto intermedio.
El techo de throughput de Pinecone es notablemente más bajo de lo que se esperaría para un servicio managed. La arquitectura serverless prioriza optimización de costo por query sobre throughput crudo.
HNSW vs IVF: Cuándo Gana Cada Uno
pgvector 0.8 soporta tanto índices HNSW como IVF-Flat. La elección importa más de lo que la mayoría cree.
HNSW (Hierarchical Navigable Small World)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 256);
-- Al momento de la query
SET hnsw.ef_search = 100;
Pros: Queries rápidas, alto recall, sin paso de entrenamiento Contras: Alto uso de memoria (~1.5x los datos de vectores), builds de índice lentos
Tiempo de build HNSW para 2.3M vectores a 1536 dimensiones: 47 minutos en nuestra instancia RDS. Uso de memoria: 14.2GB solo para el índice.
IVF-Flat (Inverted File con cuantización Flat)
CREATE INDEX ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 1500);
-- Al momento de la query
SET ivfflat.probes = 40;
Pros: Menor uso de memoria (~0.3x), builds de índice más rápidos Contras: Requiere entrenamiento (el parámetro lists necesita tuning), menor recall a la misma latencia
Tiempo de build IVF: 12 minutos. Memoria: 4.1GB. Pero el recall a la misma latencia fue 91.2% vs 97.8% de HNSW.
Recomendación
Usar HNSW a menos que la memoria sea la restricción principal. La diferencia de recall (97.8% vs 91.2%) significa que por cada 100 queries, IVF se pierde ~7 resultados relevantes que HNSW encuentra. En un pipeline RAG, ese es chunks perdidos impactan directamente la calidad de las respuestas.
Cuantización: El Tradeoff de Memoria vs Calidad
Con 2.3M vectores a 1536 dimensiones (float32), los datos crudos de vectores son aproximadamente 13.4GB. La cuantización reduce esto dramáticamente:
pgvector: halfvec (float16)
-- pgvector 0.8 soporta halfvec nativamente
ALTER TABLE documents
ALTER COLUMN embedding TYPE halfvec(1536);
CREATE INDEX ON documents
USING hnsw (embedding halfvec_cosine_ops);
Reducción de memoria: 50%. Impacto en recall: -0.8% (despreciable). Esto es un decisión evidente para usuarios de pgvector.
Qdrant: Cuantización Binaria con Rescoring
{
"collection_name": "documents",
"vectors": {
"size": 1536,
"distance": "Cosine",
"quantization_config": {
"binary": {
"always_ram": true
}
}
}
}
La cuantización binaria reduce cada float32 a un solo bit. 32x de reducción de memoria. Pero el recall crudo baja a ~85%. El truco es el rescoring: Qdrant primero busca en el índice binario para obtener un conjunto de candidatos (digamos, top-100), después re-evalúa ese es candidatos contra los vectores de precisión completa. Recall final: 94.7% con una fracción de la memoria.
En las pruebas de benchmark:
| Modo Qdrant | Memoria | Latencia p50 | Recall@10 |
|---|---|---|---|
| Precisión completa | 14.8GB | 3.1ms | 98.2% |
| Binario (sin rescore) | 0.46GB | 1.2ms | 84.9% |
| Binario + rescore 100 | 0.46GB + 14.8GB disco | 4.8ms | 94.7% |
| Escalar (int8) | 3.7GB | 2.8ms | 96.1% |
La cuantización escalar (int8) es el punto dulce para la mayoría: 75% de reducción de memoria con solo 2.1% de pérdida de recall.
La Pregunta de la Búsqueda Híbrida
Los pipelines RAG reales no hacen solo búsqueda vectorial. Necesitás matching de keywords, filtrado de metadata, y muchas veces ambos combinados.
pgvector: La Ventaja Injusta
WITH vector_results AS (
SELECT id, content,
1 - (embedding <=> $1::vector) AS vector_score
FROM documents
WHERE category = $2
ORDER BY embedding <=> $1::vector
LIMIT 20
),
text_results AS (
SELECT id, content,
ts_rank(search_vector, plainto_tsquery($3)) AS text_score
FROM documents
WHERE category = $2
AND search_vector @@ plainto_tsquery($3)
LIMIT 20
)
SELECT COALESCE(v.id, t.id) AS id,
COALESCE(v.content, t.content) AS content,
COALESCE(v.vector_score, 0) * 0.7 +
COALESCE(t.text_score, 0) * 0.3 AS combined_score
FROM vector_results v
FULL OUTER JOIN text_results t ON v.id = t.id
ORDER BY combined_score DESC
LIMIT 10;
Esta es la característica más destacada de pgvector: búsqueda híbrida en una sola query, con todo el poder del query planner de PostgreSQL. Sin orquestación externa, sin índice de keywords separado, sin fusión de resultados en código de aplicación.
Weaviate: Híbrido Built-in
{
Get {
Document(
hybrid: {
query: "configuración SSO Azure AD"
alpha: 0.7
fusionType: relativeScoreFusion
}
where: {
path: ["category"]
operator: Equal
valueText: "authentication"
}
limit: 10
) {
content
_additional { score }
}
}
}
Weaviate tiene la mejor historia de búsqueda híbrida entre las bases vectoriales dedicadas. El parámetro alpha controla el peso vector vs keyword.
Análisis de Costos (La Parte Que Decide)
Para 2.3M vectores a 1536 dimensiones, corriendo 24/7:
| Base de datos | Costo Mensual | Notas |
|---|---|---|
| pgvector (RDS r7g.xlarge) | $412 | Incluye cómputo + almacenamiento |
| pgvector (Neon Pro) | $69-190 | Escala a cero, pago por query |
| pgvector (Supabase Pro) | $75-200 | Incluye otras features de Postgres |
| Qdrant Cloud (3 nodos) | $720 | Cluster dedicado |
| Qdrant (self-hosted EC2) | $390 | 3x c7g.xlarge |
| Pinecone Serverless | $280-650 | Depende del volumen de queries |
| Weaviate Cloud | $580 | Managed 3 nodos |
pgvector en Neon o Supabase es absurdamente barato para workloads chicos a medianos. Y como es solo Postgres, no se está pagando un servicio separado — tus vectores viven al lado de tus datos relacionales.
Para workloads de alto throughput (>1000 QPS sostenido), Qdrant self-hosted en instancias ARM es el ganador en relación precio-performance.
El pricing serverless de Pinecone es atractivo a volúmenes bajos pero escala de forma impredecible. En un caso, los patrones de queries cambiaron y la factura saltó 2.3x.
Recomendaciones Prácticas
Para la mayoría de startups y MVPs: pgvector en Supabase o Neon. Teners una base de datos vectorial y una base relacional en una. La búsqueda híbrida funciona de una. La latencia es suficientemente buena. Siempre es posible migrar después si lo superás, y en la práctica, la mayoría de los productos nunca lo harán.
Para producción de alto throughput y sensible a latencia: Qdrant. El engine en Rust es genuinamente más rápido, las opciones de cuantización te dan control fino sobre el tradeoff memoria/recall, y el filtrado está bien integrado. Self-hostealo en instancias Graviton para la mejor relación precio-rendimiento.
Para equipos que quieren todo managed y no les importa el costo: Pinecone. Funciona, es simple, y no teners que pensar en infraestructura. Solo sepa que se está pagando un premium por esa simplicidad, y los cold starts serverless resultarán problemáticos.
Para aplicaciones heavy en búsqueda híbrida: Weaviate si se desea una DB vectorial dedicada, pgvector si se desea la flexibilidad de SQL.
La Verdad Incómoda
Aquí va lo que nadie en el espacio de bases vectoriales quiere admitir: para el 90% de las aplicaciones RAG, pgvector es suficiente. Las bases vectoriales dedicadas son más rápidas, pero "más rápido" significa 3ms vs 8ms — ambas son invisibles para el usuario final que está esperando 800ms a que el LLM genere una respuesta.
El diferenciador real es a escala (>10M vectores), a throughput extremo (>5000 QPS), o cuando se necesita latencia p99 sub-5ms para aplicaciones en tiempo real. Si esa es la situación, Qdrant o Weaviate valen la complejidad operacional. Si no — y ser honesto sobre si lo es — simplemente utilizar pgvector y dedicar tu tiempo de ingeniería a lo que realmente mueve la aguja: mejores estrategias de chunking, mejores prompts, y mejor evaluación.
La elección aburrida muchas veces es la correcta.