pgvector vs Milvus for batch processing: Which Should You Use?
pgvector is a PostgreSQL extension for vector search. Milvus is a purpose-built vector database built for high-throughput ANN workloads and distributed scaling.
For batch processing, use pgvector if your vectors live next to relational data and your batch jobs are moderate in size. Use Milvus if you’re pushing large embedding batches, need faster bulk ingestion, or expect the index to grow beyond what a single Postgres instance should carry.
Quick Comparison
| Area | pgvector | Milvus |
|---|---|---|
| Learning curve | Very low if you already know PostgreSQL, SQL, and CREATE EXTENSION vector | Higher. You need to learn collections, partitions, indexes, and Milvus client APIs |
| Performance | Good for small-to-medium datasets and batch jobs inside Postgres | Better for large-scale similarity search and heavy batch ingestion |
| Ecosystem | Best when your app already uses Postgres, joins, transactions, and SQL tooling | Best when vector search is a first-class system with dedicated infra |
| Pricing | Cheap to start. One database, one operational surface area | More expensive operationally. Usually more infra, more moving parts |
| Best use cases | RAG over relational data, metadata filtering, small batch embedding loads, internal tools | Large embedding pipelines, high-volume indexing, distributed search workloads |
| Documentation | Straightforward because it fits the Postgres model; fewer concepts overall | Strong docs, but more concepts: schema design, index types like HNSW/IVF_FLAT, load/release patterns |
When pgvector Wins
- •
You already run PostgreSQL in production.
If your batch job is loading embeddings alongside customer records, tickets, documents, or transactions, keep it in Postgres. With
COPY,INSERT ... ON CONFLICT, andUPDATE, you get one transactional system instead of syncing two databases. - •
Your batch sizes are manageable.
If you’re processing tens of thousands or even low millions of vectors and querying them with filters like
WHERE tenant_id = ?, pgvector is enough. The combination of SQL predicates plus vector operators like<->for L2 distance or<=>for cosine distance is exactly what you want. - •
You need relational joins in the same query.
Batch pipelines often need enrichment: join embeddings to account tables, policy metadata, or document status. With pgvector you can do this in one query instead of shipping IDs back and forth between systems.
- •
Your team wants fewer systems to operate.
For internal tools and early-stage production workloads, Postgres plus pgvector is the sane choice. You avoid running a separate vector cluster just to store embeddings that mostly sit next to structured business data.
Example pattern:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
doc_id bigint NOT NULL,
tenant_id bigint NOT NULL,
embedding vector(1536),
content text
);
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);
For batch ingest:
COPY chunks (doc_id, tenant_id, embedding, content)
FROM STDIN;
That’s hard to beat when your pipeline already speaks SQL.
When Milvus Wins
- •
You are ingesting large embedding batches continuously.
Milvus is built for this problem. If your pipeline writes hundreds of thousands or millions of vectors per run, Milvus handles bulk load and indexing more naturally than trying to stretch Postgres into a vector warehouse.
- •
You need distributed scale from day one.
Milvus is the better answer when one node is not enough. It’s designed around separating storage and compute concerns so you can scale out without turning your database into a bottleneck.
- •
Vector search is the product, not a feature.
If similarity search drives core application behavior — recommendations, semantic retrieval at scale, fraud pattern matching over embeddings — use the system made for that workload. Milvus gives you collection-level design, index tuning like HNSW or IVF-based strategies depending on deployment/version choices, and retrieval patterns that fit large ANN systems.
- •
You expect heavy concurrent search after batch indexing.
Batch processing doesn’t end at ingestion. If many workers will query freshly loaded vectors immediately after a nightly build or streaming micro-batch update cycle, Milvus handles that separation of indexing and serving better than overloading Postgres.
Example pattern:
from pymilvus import connections, Collection
connections.connect(alias="default", host="localhost", port="19530")
collection = Collection("customer_embeddings")
collection.load()
results = collection.search(
data=[query_vector],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"nprobe": 16}},
limit=10,
output_fields=["customer_id", "segment"]
)
That API makes sense when vector retrieval is its own service boundary.
For batch processing Specifically
Pick pgvector if your batch job is mostly “load embeddings + filter + join + query” inside an existing Postgres-backed system. Pick Milvus if your batch job is really an ingestion pipeline for a large vector corpus and throughput matters more than SQL convenience.
My recommendation: default to pgvector until it hurts. The moment batch ingestion volume or query fan-out starts stressing Postgres storage/indexing behavior, move the workload to Milvus instead of forcing Postgres to become a dedicated vector engine.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit