pgvector vs Milvus for batch processing: Which Should You Use?

By Cyprian AaronsUpdated 2026-04-21
pgvectormilvusbatch-processing

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

AreapgvectorMilvus
Learning curveVery low if you already know PostgreSQL, SQL, and CREATE EXTENSION vectorHigher. You need to learn collections, partitions, indexes, and Milvus client APIs
PerformanceGood for small-to-medium datasets and batch jobs inside PostgresBetter for large-scale similarity search and heavy batch ingestion
EcosystemBest when your app already uses Postgres, joins, transactions, and SQL toolingBest when vector search is a first-class system with dedicated infra
PricingCheap to start. One database, one operational surface areaMore expensive operationally. Usually more infra, more moving parts
Best use casesRAG over relational data, metadata filtering, small batch embedding loads, internal toolsLarge embedding pipelines, high-volume indexing, distributed search workloads
DocumentationStraightforward because it fits the Postgres model; fewer concepts overallStrong 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, and UPDATE, 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

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

Related Guides