Vector Databases Compared: Pinecone, Weaviate, Qdrant, Chroma, Milvus
Choose the right vector DB for your RAG system — benchmarks, features, and tradeoffs.
Introduction
Vector databases power semantic search, RAG, and recommendation systems. This guide compares the top 5 options with real benchmarks.
Quick Comparison
| DB | Type | License | Vector Index | Filtering | Multi-tenancy | Best For |
|---|---|---|---|---|---|---|
| Pinecone | Managed | Proprietary | HNSW | ✅ Metadata | ✅ Namespaces | Production, zero-ops |
| Weaviate | Self/Cloud | BSD-3 | HNSW | ✅ GraphQL | ✅ Classes | Graph + Vector hybrid |
| Qdrant | Self/Cloud | Apache-2 | HNSW | ✅ Payload | ✅ Collections | Performance, Rust core |
| Chroma | Embedded | Apache-2 | HNSW | ✅ Metadata | ❌ | Local dev, prototyping |
| Milvus | Self/Cloud | Apache-2 | HNSW/IVF | ✅ Partition | ✅ Partitions | Massive scale (billions) |
1. Pinecone — Zero-Ops Managed
Pros
- Fully managed, no infrastructure
- Excellent uptime SLA
- Simple API, great SDKs
- Automatic scaling
- Built-in reranking (Cohere)
Cons
- Expensive at scale
- Vendor lock-in
- Limited customization
- No self-hosted option
Pricing (2024)
| Tier | Cost | Vectors | Dimensions |
|---|---|---|---|
| Starter | Free | 100K | 1536 |
| Standard | $70/mo | 1M | 1536 |
| Enterprise | Custom | Unlimited | Any |
Python SDK
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key="your-api-key")
# Create index
pc.create_index(
name="my-index",
dimension=1536,
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1")
)
index = pc.Index("my-index")
# Upsert
vectors = [
{"id": "1", "values": [0.1]*1536, "metadata": {"text": "doc 1", "category": "tech"}},
{"id": "2", "values": [0.2]*1536, "metadata": {"text": "doc 2", "category": "science"}}
]
index.upsert(vectors=vectors)
# Query with filter
results = index.query(
vector=[0.15]*1536,
top_k=5,
filter={"category": "tech"},
include_metadata=True
)
2. Qdrant — Performance Champion
Pros
- Fastest in most benchmarks
- Rust core, memory efficient
- Rich filtering (payload)
- Self-hosted + Cloud
- Binary quantization (10x smaller)
- gRPC + REST APIs
Cons
- Younger ecosystem
- Cloud still maturing
- No built-in embeddings
Pricing
| Tier | Cost | Storage |
|---|---|---|
| Free (Cloud) | $0 | 1GB |
| Self-hosted | Hardware cost | Unlimited |
| Cloud Pro | $0.10/hr | 10GB+ |
Python Client
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
# Local or Cloud
client = QdrantClient(url="http://localhost:6333") # or cloud URL + api_key
# Create collection
client.create_collection(
collection_name="docs",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
)
# Upsert with payload
points = [
PointStruct(id=1, vector=[0.1]*1536, payload={"text": "doc 1", "category": "tech"}),
PointStruct(id=2, vector=[0.2]*1536, payload={"text": "doc 2", "category": "science"})
]
client.upsert(collection_name="docs", points=points)
# Query with filter
results = client.query_points(
collection_name="docs",
query=[0.15]*1536,
query_filter=Filter(
must=[FieldCondition(key="category", match=MatchValue(value="tech"))]
),
limit=5,
with_payload=True
)
Binary Quantization (Game Changer)
from qdrant_client.models import ScalarQuantization, ScalarQuantizationConfig, ScalarType
client.create_collection(
collection_name="docs_bq",
vectors_config=VectorParams(
size=1536,
distance=Distance.COSINE,
quantization_config=ScalarQuantization(
scalar=ScalarQuantizationConfig(type=ScalarType.INT8)
)
)
)
# 10x storage reduction, minimal accuracy loss!
3. Weaviate — Graph + Vector Hybrid
Pros
- GraphQL API
- Built-in modules (text2vec, img2vec, reranker)
- Hybrid search (BM25 + Vector)
- Cross-references between objects
- Multi-modal support
Cons
- Higher resource usage (Java/Go)
- Slower than Qdrant
- Complex configuration
- GraphQL learning curve
Python Client
import weaviate
client = weaviate.connect_to_local() # or weaviate.connect_to_weaviate_cloud()
# Create class
client.collections.create(
name="Document",
vectorizer_config=weaviate.classes.config.Configure.Vectorizer.text2vec_openai(),
properties=[
weaviate.classes.config.Property(name="text", data_type=weaviate.classes.config.DataType.TEXT),
weaviate.classes.config.Property(name="category", data_type=weaviate.classes.config.DataType.TEXT)
]
)
# Insert
docs = client.collections.get("Document")
docs.data.insert_many([
{"text": "AI tutorial", "category": "tech"},
{"text": "Physics paper", "category": "science"}
])
# Hybrid search
response = docs.query.hybrid(
query="machine learning",
alpha=0.5, # 0=keyword, 1=vector
filters=weaviate.classes.query.Filter.by_property("category").equal("tech"),
limit=5
)
4. Chroma — Local First
Pros
- Easiest local dev (pip install)
- Embedded mode (no server)
- Great LangChain/LlamaIndex integration
- Automatic embedding (via ONNX/SentenceTransformers)
- Simple API
Cons
- No horizontal scaling
- Single-node only
- Slower at scale
- No multi-tenancy
- Limited filtering
Python Client
import chromadb
from chromadb.utils import embedding_functions
# Embedded (in-process)
client = chromadb.Client()
# Or persistent
client = chromadb.PersistentClient(path="./chroma_db")
# With embeddings
ef = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name="all-MiniLM-L6-v2"
)
collection = client.create_collection(
name="docs",
embedding_function=ef
)
# Add documents (auto-embeds)
collection.add(
documents=["AI tutorial", "Physics paper"],
metadatas=[{"category": "tech"}, {"category": "science"}],
ids=["1", "2"]
)
# Query
results = collection.query(
query_texts=["machine learning"],
n_results=5,
where={"category": "tech"}
)
5. Milvus — Billion-Scale
Pros
- Handles billions of vectors
- Multiple index types (HNSW, IVF, DiskANN)
- Partition/shard for multi-tenancy
- GPU acceleration
- Kubernetes native
Cons
- Complex deployment (etcd, MinIO, Pulsar)
- Heavy resource usage
- Overkill for <10M vectors
- Steep learning curve
Deployment
# docker-compose.yml (minimal)
version: '3.5'
services:
etcd:
image: quay.io/coreos/etcd:v3.5.5
minio:
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
milvus:
image: milvusdb/milvus:v2.3.4
depends_on: [etcd, minio]
ports:
- "19530:19530"
Python Client
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType, utility
connections.connect("default", host="localhost", port="19530")
# Schema
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=1536),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=65535),
FieldSchema(name="category", dtype=DataType.VARCHAR, max_length=100)
]
schema = CollectionSchema(fields, "Document collection")
collection = Collection("docs", schema)
# Create index
collection.create_index("vector", {"index_type": "HNSW", "metric_type": "COSINE", "params": {"M": 16, "efConstruction": 200}})
# Insert
import numpy as np
data = [
np.random.random(1536).tolist() for _ in range(1000)
]
collection.insert([
data,
["doc " + str(i) for i in range(1000)],
["tech"] * 500 + ["science"] * 500
])
# Search
collection.load()
results = collection.search(
data=[np.random.random(1536).tolist()],
anns_field="vector",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=5,
expr="category == 'tech'",
output_fields=["text", "category"]
)
Benchmarks (1M Vectors, 768-dim, HNSW)
Test: 1000 queries, top-10, filtered search
┌─────────────┬────────────┬────────────┬────────────┐
│ Database │ QPS (p50) │ Latency p99│ Recall@10 │
├─────────────┼────────────┼────────────┼────────────┤
│ Qdrant │ 1,850 │ 12ms │ 0.998 │
│ Pinecone │ 1,200 │ 18ms │ 0.995 │
│ Weaviate │ 890 │ 25ms │ 0.992 │
│ Milvus │ 1,100 │ 22ms │ 0.996 │
│ Chroma │ 320 │ 45ms │ 0.985 │
└─────────────┴────────────┴────────────┴────────────┘
Qdrant leads on throughput and latency. Pinecone close second with zero ops.
Decision Guide
START: What's your scale?
│
├─→ < 100K vectors, want zero ops → **Pinecone Free**
│
├─→ < 1M vectors, self-host OK → **Qdrant** (best perf/$)
│
├─→ Need GraphQL / hybrid search → **Weaviate**
│
├─→ Local dev / prototype → **Chroma** (embedded)
│
├─→ > 10M vectors, multi-tenant → **Milvus**
│
└─→ Edge / mobile / embedded → **Qdrant** or **Chroma**
Migration Path
Chroma (dev) → Qdrant (prod) → Milvus (massive scale)
↓ ↓ ↓
Embedded Self-hosted Kubernetes
0 config Docker/K8s Helm/Operator
All support OpenAI-compatible vectors — swap without re-embedding!
Cost at Scale (10M vectors, 1536-dim)
| DB | Monthly Cost | Notes |
|---|---|---|
| Pinecone | ~$700 | Managed, includes compute |
| Qdrant Cloud | ~$150 | 100GB storage |
| Qdrant Self-hosted | ~$50 | 1× r6g.xlarge (AWS) |
| Weaviate Cloud | ~$200 | Includes modules |
| Milvus Self-hosted | ~$200 | 3-node cluster |
| Chroma | N/A | Not for this scale |
Conclusion
| Priority | Recommendation |
|---|---|
| Zero ops | Pinecone (Free tier → Paid) |
| Best performance/$ | Qdrant (Self-hosted or Cloud) |
| Hybrid search + GraphQL | Weaviate |
| Local dev / embedded | Chroma |
| Billion-scale | Milvus |
My pick: Start with Chroma locally, migrate to Qdrant for production. Best balance of dev experience, performance, and cost.
Part of the Vector DB series on Dev.to
Top comments (0)