<!-- Canonical: https://lucioduran.com/blog/vector-databases-pgvector-qdrant-pinecone
Language: en
Content-SHA256: fb18470529f92142795b0ae5de279a45bca3ee9f2d514a57fe6808a6fe2386d7
-->

AI/ML

# Vector Databases Compared: Running the Same RAG Pipeline on pgvector, Qdrant, Pinecone, and Weaviate

2025-12-029 min read

Lucio Durán
Engineering Manager & AI Solutions Architect

Also available in: [Español](<https://lucioduran.com/es/blog/vector-databases-pgvector-qdrant-pinecone>)

## The Test Setup

**Dataset**: 2.3M document chunks from a customer support knowledge base. Average chunk size: 512 tokens. Embedded with ` text-embedding-3-small ` (1536 dimensions).

**Queries**: 10,000 real user queries collected from production logs (anonymized). Mix of short ("reset password"), medium ("how to configure SSO with Azure AD"), and long queries ("our team is migrating from on-premise to cloud and we need to understand the data residency implications for GDPR compliance").

**Hardware**:

- pgvector: RDS ` db.r7g.xlarge ` (4 vCPU, 32GB RAM), PostgreSQL 16 with pgvector 0.8
- Qdrant: 3-node cluster on ` c7g.xlarge ` (4 vCPU, 8GB RAM each), Qdrant 1.13
- Pinecone: Serverless (Standard), us-east-1
- Weaviate: 3-node cluster on ` c7g.xlarge `, Weaviate 1.28

**Search**: Top-10 nearest neighbors, cosine similarity, with metadata filter (category = specific value, reducing candidates by ~80%).

## Raw Numbers: Latency and Throughput

### Query Latency (10K queries, ms)

| Database | 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](<https://qdrant.tech/>) wins on raw latency, and it's not close. Their Rust-based engine with memory-mapped storage is genuinely fast. But look at Pinecone's p99.9, that 142ms tail is the serverless cold start biting you. For interactive applications, that tail is a problem.

pgvector with HNSW is surprisingly competitive. At 8.2ms p50, that's fast enough for any RAG application. The gap widens at the tail, but for most use cases, this is more than adequate.

### Throughput (queries/second, 100 concurrent connections)

| Database | QPS (no filter) | QPS (with filter) | 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% |

The filtering performance difference is revealing. pgvector loses 25% throughput with filters because it applies them post-HNSW search (a known limitation). Qdrant integrates filtering into the search algorithm, so the penalty is smaller. Weaviate falls somewhere in between.

Pinecone's throughput ceiling is notably lower than what might be expected for a managed service. The serverless architecture prioritizes per-query cost optimization over raw throughput.

## HNSW vs IVF: When Each Wins

pgvector 0.8 supports both HNSW and IVF-Flat indexes. The choice matters more than most people realize.

### HNSW (Hierarchical Navigable Small World)

```sql
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 256);

-- At query time
SET hnsw.ef_search = 100;
```

**Pros**: Fast queries, high recall, no training step **Cons**: High memory usage (~1.5x the vector data), slow index builds

HNSW build time for 2.3M vectors at 1536 dimensions: **47 minutes** on our RDS instance. Memory usage: **14.2GB** for the index alone.

### IVF-Flat (Inverted File with Flat quantization)

```sql
CREATE INDEX ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 1500);

-- At query time
SET ivfflat.probes = 40;
```

**Pros**: Lower memory usage (~0.3x), faster index builds **Cons**: Requires training (the lists parameter needs tuning), lower recall at the same latency

IVF build time: **12 minutes**. Memory: **4.1GB**. But recall at the same latency was 91.2% vs HNSW's 97.8%.

### Recommendation

Use HNSW unless memory is the binding constraint. The recall difference (97.8% vs 91.2%) means that for every 100 queries, IVF misses ~7 relevant results that HNSW finds. In a RAG pipeline, those missed chunks directly impact answer quality.

## Quantization: The Memory vs Quality Tradeoff

At 2.3M vectors with 1536 dimensions (float32), the raw vector data is about **13.4GB**. Quantization reduces this dramatically:

### pgvector: halfvec (float16)

```sql
-- pgvector 0.8 supports halfvec natively
ALTER TABLE documents
 ALTER COLUMN embedding TYPE halfvec(1536);

-- Index works the same
CREATE INDEX ON documents
USING hnsw (embedding halfvec_cosine_ops);
```

Memory reduction: 50%. Recall impact: **-0.8%** (negligible). This is a no-brainer for pgvector users. Half-precision floats lose almost nothing for similarity search.

### Qdrant: Binary Quantization with Rescoring

```json
{
 "collection_name": "documents",
 "vectors": {
 "size": 1536,
 "distance": "Cosine",
 "quantization_config": {
 "binary": {
 "always_ram": true
 }
 }
 },
 "quantization_config": {
 "binary": {
 "always_ram": true
 }
 }
}
```

Binary quantization reduces each float32 to a single bit. 32x memory reduction. But raw recall drops to ~85%. The trick is **rescoring**: Qdrant first searches the binary index to get a candidate set (say, top-100), then rescores those candidates against the full-precision vectors. Final recall: **94.7%** at a fraction of the memory.

In benchmark testing:

| Qdrant Mode | Memory | p50 Latency | Recall@10 |
| --- | --- | --- | --- |
| Full precision | 14.8GB | 3.1ms | 98.2% |
| Binary (no rescore) | 0.46GB | 1.2ms | 84.9% |
| Binary + rescore 100 | 0.46GB + 14.8GB disk | 4.8ms | 94.7% |
| Scalar (int8) | 3.7GB | 2.8ms | 96.1% |

The scalar quantization (int8) is the sweet spot for most workloads: 75% memory reduction with only 2.1% recall loss.

### Pinecone: Automatic Quantization

Pinecone serverless applies quantization automatically and opaquely. You don't control it. In our tests, their recall was 93.8%, suggesting aggressive quantization behind the scenes. For some applications this is fine; for others, the lack of control is frustrating.

## The Hybrid Search Question

Real RAG pipelines don't just do vector search. You need keyword matching, metadata filtering, and often both combined. Here's where the architectures diverge sharply.

### pgvector: The Unfair Advantage

```sql
-- Hybrid search: vector similarity + full-text search + metadata filter
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;
```

This is pgvector's killer feature: hybrid search in a single query, with the full power of PostgreSQL's query planner. No external orchestration, no separate keyword index, no result fusion in application code.

### Qdrant: Payload Filtering

```python
from qdrant_client import QdrantClient, models

results = client.search(
 collection_name="documents",
 query_vector=query_embedding,
 query_filter=models.Filter(
 must=[
 models.FieldCondition(
 key="category",
 match=models.MatchValue(value="billing")
 )
 ]
 ),
 limit=10,
 search_params=models.SearchParams(
 hnsw_ef=100,
 exact=False
 )
)
```

Qdrant handles metadata filtering well (integrated into the HNSW search), but keyword search requires a separate full-text index and manual fusion. For simple filter + vector search, it's excellent. For true hybrid search, you're building the fusion logic yourself.

### Weaviate: Built-in Hybrid

```graphql
{
 Get {
 Document(
 hybrid: {
 query: "SSO Azure AD configuration"
 alpha: 0.7
 fusionType: relativeScoreFusion
 }
 where: {
 path: ["category"]
 operator: Equal
 valueText: "authentication"
 }
 limit: 10
 ) {
 content
 _additional {
 score
 }
 }
 }
}
```

Weaviate has the best hybrid search story among the dedicated vector databases. The ` alpha ` parameter controls the vector vs keyword weight, and ` relativeScoreFusion ` handles the score normalization. It's not quite as flexible as raw SQL, but it's production-ready out of the box.

## Cost Analysis (The Part That Decides)

For 2.3M vectors at 1536 dimensions, running 24/7:

| Database | Monthly Cost | Notes |
| --- | --- | --- |
| pgvector (RDS r7g.xlarge) | $412 | Includes compute + storage |
| pgvector (Neon Pro) | $69-190 | Scales to zero, pay per query |
| pgvector (Supabase Pro) | $75-200 | Includes other Postgres features |
| Qdrant Cloud (3-node) | $720 | Dedicated cluster |
| Qdrant (self-hosted EC2) | $390 | 3x c7g.xlarge |
| Pinecone Serverless | $280-650 | Depends on query volume |
| Weaviate Cloud | $580 | Managed 3-node |

pgvector on [Neon](<https://neon.com/>) or [Supabase](<https://supabase.com/>) is absurdly cheap for small-to-medium workloads. And because it's just Postgres, you're not paying for a separate service, your vectors live next to your relational data.

For high-throughput workloads (\>1000 QPS sustained), Qdrant self-hosted on ARM instances is the price-performance winner.

Pinecone's serverless pricing is attractive at low volumes but scales unpredictably. In one case, query patterns changed and the bill jumped 2.3x.

## Practical Recommendations

**For most startups and MVPs**: pgvector on Supabase or Neon. You get a vector database and a relational database in one. Hybrid search works out of the box. The latency is good enough. You can always migrate later if you outgrow it, and in practice, most products never will.

**For high-throughput, latency-sensitive production**: Qdrant. The Rust engine is genuinely faster, the quantization options give you fine-grained control over the memory/recall tradeoff, and the filtering is well-integrated. Self-host on Graviton instances for the best price-performance.

**For teams that want managed everything and don't mind the cost**: Pinecone. It works, it's simple, and you don't have to think about infrastructure. Just know that you're paying a premium for that simplicity, and the serverless cold starts will bite you.

**For hybrid search-heavy applications**: Weaviate if you want a dedicated vector DB, pgvector if you want the flexibility of SQL. Weaviate's GraphQL API for hybrid search is well-designed and saves development time.

## The Uncomfortable Truth

Here's what nobody in the vector database space wants to admit: for 90% of RAG applications, pgvector is sufficient. The dedicated vector databases are faster, but "faster" means 3ms vs 8ms, both are invisible to the end user who's waiting 800ms for the LLM to generate a response.

The real differentiator is at scale (\>10M vectors), at extreme throughput (\>5000 QPS), or when you need sub-5ms p99 latency for real-time applications. If that's you, Qdrant or Weaviate are worth the operational complexity. If it's not, and be honest about whether it is, just use pgvector and spend your engineering time on what actually moves the needle: better chunking strategies, better prompts, and better evaluation.

The boring choice is often the right one.

## Frequently asked questions

### Should I use a dedicated vector database or just pgvector?

For most applications under 5M vectors, pgvector is the right choice, you avoid operational complexity and can do hybrid queries joining vectors with relational data. Beyond 10M vectors or if you need sub-10ms p99 latency, dedicated vector databases start to justify their complexity.

### What embedding dimension should I use?

For most RAG applications, 768 or 1024 dimensions from a model like text-embedding-3-small is the sweet spot. Going to 1536 or 3072 dimensions rarely improves retrieval quality enough to justify the 2-4x storage and latency cost.

### How much does vector search quality degrade with quantization?

With scalar quantization (float32 to int8), recall drops 1-3%. With binary quantization plus rescoring, recall drops only 2-5% while reducing memory by 32x. Product quantization can lose 5-10% recall but saves the most space.

## Tips

1. **pgvector win:** Use ` HNSW ` indexes with ` vector_cosine_ops ` and set ` hnsw.ef_search = 100 ` at session level, the default of 40 sacrifices too much recall for most RAG use cases.
2. **Quantization secret:** Binary quantization on Qdrant with rescoring gives you 95% of full-precision recall at 32x less memory. Set ` always_ram: true ` for the rescoring index.
3. **Pinecone gotcha:** Serverless indexes have a cold-start penalty of 200-500ms on first query after idle. If latency matters, keep a heartbeat query running every 30 seconds.
4. **Cost trick:** For datasets under 500K vectors, pgvector on a ` db.r7g.large ` RDS instance beats every managed vector DB on both price and latency. Don't overcomplicate it.

- vector-databases
- pgvector
- qdrant
- pinecone
- weaviate
- rag
- embeddings
- hnsw
- ai

[Back to Blog](<https://lucioduran.com/blog>)
