Dev.to AI 🤖 Ai 👁 0 📖 23 min read

Enterprise Vector Database 2026: Qdrant vs Milvus vs pgvector vs Pinecone

10M+ Vector Benchmark, Memory/Cost TCO & Hybrid Search Architecture Blueprint Published: September 2026 | Target workload: 10 M vectors × 1 536-dim (OpenAI ada-002), ~1 KB metadata/record, 100–300 QPS sustained, p99 <

Enterprise Vector Database 2026: Qdrant vs Milvus vs pgvector vs Pinecone

Self-Hosted LLM Gateway Architecture 2026

10M+ Vector Benchmark, Memory/Cost TCO & Hybrid Search Architecture Blueprint

Published: September 2026 | Target workload: 10 M vectors × 1 536-dim (OpenAI ada-002), ~1 KB metadata/record, 100–300 QPS sustained, p99 < 100 ms, Recall@10 ≥ 0.95.

Data currency notice: All cloud prices are USD list prices collected 2026-09-16. Prices change frequently — verify exact rates before publishing.

Bottom Line: For enterprise 10M×1536 workloads in 2026, Qdrant delivers the best overall balance of performance (highest QPS and lowest p99 at equal recall), TCO ($388–947/mo self-hosted, $250–450/mo managed), and native hybrid search — while Milvus/Zilliz is the pick for maximum index flexibility, pgvector for teams already on PostgreSQL with modest QPS needs, and Pinecone for zero-ops serverless whose read-unit costs scale with index size.

Table of Contents

  1. Executive Summary
  2. Engine Profiles at a Glance
  3. Performance Benchmarks
  4. Comparison Matrix
  5. TCO Analysis
  6. Hybrid Search Architecture Blueprint
  7. Production Configuration Reference
  8. Decision Framework: When to Choose What
  9. Migration & Architecture Guidance
  10. Appendix: Sources & Caveats

1. Executive Summary

Choosing the right vector database for enterprise workloads in 2026 requires balancing raw performance, total cost of ownership, operational complexity, and architectural fit. This guide provides a production-grade comparison of four leading engines — Qdrant, Milvus, pgvector, and Pinecone — at the 10M+ vector scale with 1 536-dimensional embeddings.

Key findings:

Dimension Leader Why
Raw throughput at equal recall Qdrant 5–6× higher QPS than Milvus in open benchmarks at identical hardware and precision
Lowest TCO (managed) Qdrant Cloud $250–450/mo vs $600+ for Zilliz, $1,300+ for Pinecone at 10M×1536
Lowest TCO (self-hosted) Qdrant OSS $388/mo single-node AWS; $777/mo HA — lowest RAM footprint
Fastest index build Milvus 70 s for 1M×1536 vs 666–1 386 s for Qdrant (measured)
Easiest operational start pgvector Extension on existing PostgreSQL — zero new infrastructure
Zero-ops serverless Pinecone Fully managed, auto-scaling, but read costs scale with index size
Richest index variety Milvus IVF, HNSW, DiskANN, SCANN, GPU-CAGRA, sparse inverted, binary
Strongest ACID guarantees pgvector Full PostgreSQL transactional semantics, PITR, read replicas
Native hybrid search Qdrant, Milvus, Pinecone Built-in dense + sparse fusion; pgvector requires app-level integration

Bottom line for enterprise buyers:

  • Performance-first, cost-conscious teams → Qdrant (managed or self-hosted) delivers the best price/performance ratio at 10M scale.
  • Existing PostgreSQL shops → pgvector eliminates infrastructure sprawl but accepts lower ANN QPS ceiling.
  • Maximum index flexibility and managed Milvus → Zilliz Cloud offers the richest index zoo with Milvus compatibility.
  • Zero-ops with predictable serverless billing → Pinecone, but watch read-unit costs at 10M×1536 where each query scans ~72 GB.

2. Engine Profiles at a Glance

Qdrant

Open-source (Apache 2.0), written in Rust. Single binary, Docker/Helm deployment. Native filterable HNSW (no classic pre/post filter dichotomy — query planner chooses strategy). Native BM25 sparse search for hybrid retrieval. Managed Cloud with Standard/Hybrid/Private options.

Strengths: Highest published latency/throughput at equal recall; native filterable HNSW; simple ops; quantization options (SQ, BQ, PQ).

Watch out: Smaller ecosystem than PostgreSQL; fewer index types than Milvus.

Milvus

Open-source (Apache 2.0), written in Go/C++. Distributed architecture with separate coordinator, query, and data nodes. Richest index zoo: IVF_FLAT, IVF_SQ8, IVF_PQ, HNSW, DiskANN, SCANN, GPU-CAGRA, sparse inverted, binary. Managed path via Zilliz Cloud.

Strengths: Fastest index build times; cheapest storage ($0.025/GB/mo on Zilliz); tunable consistency (Strong/Bounded/Sessionizable/Eventually); DiskANN for disk-based ANN.

Watch out: Highest operational complexity for self-hosted; multi-component k8s footprint.

pgvector

PostgreSQL extension (open-source). Leverages existing Postgres infrastructure — every managed PG provider (RDS, AlloyDB, Cloud SQL, Supabase, Neon) hosts it. HNSW + IVFFlat index types, halfvec, binary quantization, sparse vectors.

Strengths: Full ACID transactions; zero new infrastructure; 40+ language client support; PITR via WAL archiving; Postgres FTS for hybrid.

Watch out: Materially lower ANN QPS at 10M (post-filter semantics, memory-hungry HNSW); no built-in hybrid fusion.

Pinecone

Fully managed serverless (proprietary). Auto-scaling, auto-indexing. Hybrid search (dense + sparse), full-text index, namespaces for data partitioning. Enterprise plan with Dedicated Read Nodes (DRNs) and BYOC option.

Strengths: Zero-ops; auto-scaling; strong SLA (99.95% Enterprise); hybrid search out of the box.

Watch out: Serverless read units (RU) scale with index size — 1 RU/GB scanned per query. At 10M×1536, each query scans ~72 GB = ~72 RU, making high-QPS workloads expensive.

2.5 Trade-offs & Limitations

Every engine choice means accepting a counterweight. Below is a neutral statement of what you give up for each engine's strengths — no favorable claim above should be read without its limitation.

Engine What you lose / hard limitations
Qdrant Smaller ecosystem and talent pool than PostgreSQL; fewer index types than Milvus precisely because HNSW + quantization covers the common case — no DiskANN/SCANN/GPU index options; no dedicated disk-based ANN for cold tiers (RAM-first with mmap); managed Cloud is a proprietary control plane → plan an exit path if lock-in matters; self-hosted HA requires operating a 3-node + replication cluster; younger project (2020) than Postgres.
Milvus Highest operational complexity when self-hosted — distributed k8s footprint (coordinator + query + data + index nodes); more moving parts for capacity planning; measured latency at equal precision in the open harness was 4–70× higher than Qdrant at 1M×1536 (576.7 ms vs 8.7 ms p99); sustained throughput under concurrent ingestion degrades up to 9×; managed Zilliz removes ops but adds a vendor dependency.
pgvector Materially lower ANN throughput ceiling at 10M scale (estimated ~30–100 QPS vs 150–300 for Qdrant); largest HNSW memory footprint of the four (~90–130 GB incl. Postgres buffers/MVCC); filtered ANN uses post-filter semantics → latency spikes on filtered workloads; no built-in hybrid fusion — must hand-roll RRF on top of FTS in application code; vertical scaling only without Citus/PgDog sharding; HNSW index updates rewrite large regions (write amplification).
Pinecone Serverless read cost scales linearly with index size (1 RU/GB scanned): at 10M×1536 every query costs ~$0.0012 in read units, so high-QPS loads run into the thousands per month; no ACID transactions; eventually-consistent by default; metadata payload size limits (40 KB) and filter-field indexing constraints; proprietary managed format → real migration/export cost if you want to leave; top-tier SLA (99.95%) requires Enterprise (≥ $500/mo).

Cross-cutting caveats that apply to all four engines:

  • Recall is a knob, not a property. Any engine can be pushed to Recall@10 = 1.0 by raising ef_search/nprobe at the cost of QPS — benchmark comparisons only mean something at equal precision.
  • Benchmarks understate production. Sustained ingestion, filter-heavy query mixes, and cross-AZ network hops all degrade published numbers by 1.1–9× (see §3.2).
  • The ops cost of being production-grade (backups, monitoring, failover, capacity planning) is not priced into any per-GB number here — factor in engineering time, not just instance cost.

3. Performance Benchmarks

3.1 Measured: Same Harness, Same Hardware

Source: Qdrant open benchmark harness (June 2024). Hardware: Azure Standard D8s v3 (8 vCPU / 32 GiB), Docker, 25 GB memory cap, official Python SDKs.

1M × 1 536-dim, cosine (dbpedia-openai) — closest proxy to target dimensions:

Engine Config Recall QPS (100 threads) p99 (ms) QPS (1 thread) p99 (ms) Index + Upload (s)
Qdrant HNSW m64 ef512 (SQ) 0.9903 1 215 8.7 290 3.7 1 386
Qdrant HNSW m16 ef128 0.9666 1 261 8.1 ~320 ~3.4 ~666
Milvus HNSW m16 ef128 0.9973 219 576.7 68 27.5 70
Weaviate HNSW m32 ef128 0.9950 845.5 201.1 50 24.0 994
Redis HNSW m32 ef512 0.9916 337 356.1 193 7.0

Note: Milvus had a single tested config in this harness and was not tuned like Qdrant. Milvus was the fastest indexer; Qdrant achieved highest RPS/lowest latency at equal precision.

10M × 96-dim, cosine (deep-image) — only true 10M dataset:

Engine Config Recall QPS (100 threads) p99 (ms) Index + Upload (s)
Qdrant HNSW m32 ef512 0.9504 1 502 71.6 663
Milvus HNSW m16 ef128 0.9865 395 378.6 908
Weaviate HNSW m64 ef512 0.9516 562 1 328 18 620
Redis HNSW m64 ef512 0.9516 690 155.6 40 279

Dimension scaling note: At 1 536-dim, both distances per query and index build cost roughly 4–16× more than at 96-dim. The 10M×1 536 numbers below are extrapolated from these measured bases.

3.2 Managed SaaS: VectorDBBench Leaderboard (2026)

Source: VectorDBBench (Zilliz open-source tool). Dataset: Cohere 1M × 768-dim. Fixed $1 000/mo budget. Note: different dims than target.

System (sized to ~$1 000/mo) Recall QPS p99 (ms)
Zilliz Cloud 8 CU (performance) 0.9383 13 316 2.0
Milvus 16c64g (SQ4 fp16) 0.9189 9 576 2.3
OpenSearch 16c128g 0.9066 3 055 7.2
Qdrant Cloud 16c64g 0.9474 1 242 6.4
Pinecone p2.x8 (1 node) 0.9262 1 147 13.7

Critical finding — ingestion pressure destroys QPS:

System Static QPS (zero ingest) QPS under 500 rows/s ingestion Degradation
Zilliz Cloud 7 385 2 119 3.5×
Milvus 2 747 306 9.0×
Pinecone 1 131 367 3.1×
Qdrant Cloud 447 394 1.1×

Any benchmark published as "10M QPS" must state whether ingestion was running — this is the single largest hidden variable.

3.3 Target Scenario Estimate — 10M × 1 536-dim

⚠️ ESTIMATED / EXTRAPOLATED. Basis: (a) 1M×1 536 measured data scaling search cost ~×5–8 for 10× vectors; (b) 10M×96 measured as lower bound; (c) VectorDBBench 10M streaming run. Single-node equivalent = 8–16 vCPU, 64–128 GB RAM.

Engine Est. Sustained QPS (100-conc) p99 ANN (ms) p99 Filtered ANN (ms) p99 Hybrid (ms) Recall@10
Qdrant ~150–300 ~15–40 ~20–60 (native filterable HNSW) ~30–80 (native dense+sparse RRF) 0.95–0.99
Milvus ~60–150 ~40–120 ~60–200 (filter expression) ~80–250 (hybrid RRF/weighted) 0.95–0.99
pgvector ~30–100 ~40–150 ~80–400 (post-filter, iterative scans) ~100–500 (FTS + RRF, no built-in fusion) 0.90–0.98
Pinecone compute-unconstrained; per-query RU cost scales with index size ~20–80 ~30–100 (metadata filter) ~40–120 (native sparse+dense) 0.90–0.99

Verification recommendation: Run vendor PoCs with your own data and query mix before publishing absolute QPS. State hardware, precision threshold, ingestion pressure, and filter ratio whenever quoting any of these.

3.4 Index Build Time — 10M × 1 536-dim

Engine Measured Base (harness) Extrapolated 10M×1 536 Notes
Qdrant 1M×1 536 → ~666–1 386 s ~1.5–4 h PQ/SQ/BQ speed build 2–10×
Milvus 1M×1 536 → 70 s ~0.5–1.5 h Fastest indexer in harness
pgvector n/a (docs: parallel workers, m/ef_construction) ~1–3 h Needs maintenance_work_mem, CREATE INDEX CONCURRENTLY
Pinecone n/a (serverless import) Ingestion via import $0.25/GB; index build managed Use bulk import, not upserts, for 10M rows

3.5 RAM & Disk — 10M × 1 536-dim float32

Raw vector bytes: 61.4 GB (10 000 000 × 1 536 × 4 B). Reference: 1M×1 536 needs ~8.6 GB incl. HNSW index (~1.4× raw); Pinecone billing example: 10M×1 536 + 1 KB metadata = 71.5 GB billed storage.

Engine RAM Working Set (HNSW in-memory) With Quantization Disk (data + index + overhead)
Qdrant ~86–100 GB SQ ~30–45 GB · BQ ~15–25 GB ~110–160 GB
Milvus ~85–100 GB IVF_SQ8/fp16 ~30–50 GB; mmap/DiskANN lower-RAM ~110–160 GB
pgvector ~90–130 GB (incl. Postgres buffers, MVCC) halfvec / binary-quantize ~40–60 GB ~130–180 GB (TOAST, WAL, MVCC)
Pinecone Managed by Pinecone (serverless) BQ in-serverless (managed) 71.5 GB billed (~$0.33/GB/mo)

Rule of thumb: Budget ≥ 100 GB RAM for full in-memory ANN at 10M×1 536 HNSW, or adopt SQ/BQ (post-filter + re-rank) to fit in 16–64 GB.

4. Comparison Matrix

4.1 Performance & Scalability Matrix

Dimension Qdrant Milvus pgvector Pinecone
Peak QPS (measured, 1M×1 536) 1 261 (100t, recall 0.967) 219 (100t, recall 0.997) n/a in harness n/a in harness
Est. QPS (10M×1 536, 100-conc) 150–300 60–150 30–100 compute-unconstrained (RU-billed)
p99 latency (measured, 1M×1 536) 8.1 ms 576.7 ms n/a n/a
Est. p99 latency (10M×1 536) 15–40 ms 40–120 ms 40–150 ms 20–80 ms
Recall@10 range 0.95–0.99 0.95–0.99 0.90–0.98 0.90–0.99
Index build (10M×1 536 est.) 1.5–4 h 0.5–1.5 h 1–3 h managed (bulk import)
Max cluster scale Distributed (shards + RF) Distributed (segments + replicas) PG replication / Citus Auto-sharded (serverless)
Ingestion QPS degradation ~1.1× (minimal) ~9.0× (severe) N/A (Postgres writes) ~3.1×

4.2 Architecture & Features Matrix

Dimension Qdrant Milvus pgvector Pinecone
Index types HNSW, SQ, BQ, PQ IVF_FLAT, IVF_SQ8, IVF_PQ, HNSW, DiskANN, SCANN, GPU-CAGRA, sparse inverted, binary HNSW, IVFFlat, halfvec, binary quant, sparsevec Managed HNSW, sparse, full-text
Filtering Native filterable HNSW (query-planner driven) Scalar indexes + expression filter; pre/post filter options Post-filter inside ANN + B-tree/partial/partition Metadata pre-filter with scalar indexes; namespaces
Native hybrid (dense + sparse) ✅ Dense + BM25 in one collection, native RRF ✅ Hybrid dense+sparse with RRF/weighted; BM25 in 2.4.5+ ❌ No built-in fusion — app-level FTS + RRF ✅ Single hybrid index (dense+sparse), full-text
Consistency models Strong (majority quorum, Raft); configurable Tunable: Strong, Bounded, Sessionizable, Eventually Strong, ACID (PostgreSQL) Eventually consistent; single-record read-your-write
Replication & sharding Shards per collection (reshardable), RF Sharded (segment/partition), replica groups Standard PG replication (WAL/streaming) Fully managed, auto-sharded
Quantization SQ, BQ, PQ (post-filter + re-rank) IVF_SQ8, fp16, SQ4, DiskANN halfvec, binary quantize Managed BQ in serverless
GPU acceleration Cloud only GPU-CAGRA index
Disk-based ANN ❌ (RAM-first with mmap) ✅ DiskANN Partial (IVFFlat on disk) Managed (serverless)

4.3 Operations & Compliance Matrix

Dimension Qdrant Milvus pgvector Pinecone
Deployment complexity Light (Docker/Helm) Highest (k8s multi-component) Simplest (Postgres extension) Zero-ops (serverless)
Min production cluster 3 nodes, RF ≥ 2 ≥ 3 components (distributed) 1 primary + 1 replica Serverless n/a; Enterprise min $500/mo
Backup / restore Snapshots via API/S3; managed DR milvus-backup + volume snapshots; Zilliz PITR pg_dump + WAL archive = true PITR Backups $0.10/GB/mo; restore $0.15/GB
Monitoring Prometheus, Datadog, Cloud Prometheus Prometheus + Grafana (official dashboards) pg_stat_statements, PgHero, CloudWatch Console metrics; Prometheus & Datadog (Builder+)
Compliance SOC 2, GDPR, HIPAA (Cloud) SOC 2 II, ISO 27001, GDPR; HIPAA-ready Whatever your PG provides (SOC2 on RDS/AlloyDB) SOC 2, ISO 27001, HIPAA add-on $190/mo
SDK languages Python, TS/JS, Java, Go, Rust, .NET, PHP, Ruby + REST/gRPC Python, Java, Go, Node.js + community C#/Rust + REST Every Postgres client (40+ languages) Python, JS/TS, Java, Go, .NET + REST
License Apache 2.0 Apache 2.0 PostgreSQL License Proprietary

4.4 Pricing & TCO Matrix

Dimension Qdrant Milvus / Zilliz pgvector / RDS Pinecone
Managed monthly (10M×1 536) ~$250–450 (Cloud) ~$600 (Zilliz 3 CU cap-opt) ~$850–900 (RDS) ~$1 300 (1M queries) / ~$12 300 (10M queries)
Self-hosted AWS (single) ~$388 ~$756 ~$777 n/a
Self-hosted AWS (HA) ~$777 ~$1 900 ~$1 554 n/a
Self-hosted GCP (single) ~$473 ~$790 ~$503 n/a
Self-hosted GCP (HA) ~$947 ~$1 169 ~$1 006 n/a
Storage cost Included in compute $0.025/GB/mo (cheapest) Included in EBS/RDS $0.33/GB/mo
Pricing model Usage-based (vCPU + RAM + disk) CU-based (capacity or performance tier) Instance-based (fixed compute) Serverless: storage + read units + write units
Free tier 0.5 vCPU / 1 GB Limited RDS starting tiers $50/mo minimum

5. TCO Analysis

5.1 Assumptions

  • Month = 730 h. All prices USD, Sept 2026 list prices, on-demand.
  • Workload: 10 M vectors, 1 536-dim, ~1 KB metadata ≈ 71.5 GB payload.
  • Self-hosted sizing: Single-node minimum production (1×) and HA (2–3× nodes).
  • Query traffic scenarios: 10 M queries/mo and 100 M queries/mo.
  • Egress: Assumed negligible (intra-region / under free tiers).

5.2 Managed SaaS — Monthly Cost Comparison

Engine Pricing Model Monthly Cost @ 10M×1 536, ~100–300 QPS Includes
Qdrant Cloud Usage-based: vCPU + RAM + disk billed hourly ~$250–450 (estimate — verify via cloud.qdrant.io/calculator) Calculator-driven; marketplace billing adds $0.01 RU units
Zilliz Cloud Dedicated CU $0.273/h (capacity-optimized) or $0.41/h (performance); storage $0.025/GB/mo ~$600 (3 CU capacity-optimized, holds ~10M×1 536); perf tier ~$1 993 (10 CU) 3 CUs hold 10M; cheapest storage per GB
Pinecone Serverless Storage $0.33/GB/mo; Read units $16–18/M RU (1 RU ≈ 1 GB scanned/query); Write units $4–4.50/M WU; Egress $0.10/GB (100 GB/mo incl.); $50/mo min Storage $23.60 + reads: 10M queries × ~72 RU × $17/M ≈ $12 264 → total ~$12 290 at 10M queries; ~$1 274 at 1M queries; $50 if idle Reads dominate at 10M×1 536 (72 RUs/query)
pgvector (RDS) RDS instance (db.r6i) + storage ~$850–900 (db.r6i.2xlarge); HA ~$1 650–1 750 (Multi-AZ) Approximate — RDS pricing varies by license/storage class

5.3 Self-Hosted — AWS EC2 (us-east-1)

Engine Recommended Node EC2 + EBS Single-Node Monthly HA Monthly
Qdrant OSS r6i.2xlarge (8 vCPU/64 GB) ×1–3 $0.504/h → $368 + 256 GB EBS $20.5 ~$388 ~$777 (×2, RF=2)
Milvus OSS 2× r6i.4xlarge (16/128) + r6i.2xlarge coordinator 2×$736 + $368 + 3×256 GB EBS $61.5 standalone r6i.4xlarge ~$756 ~$1 900 (2 query + 1 coord)
pgvector r6i.4xlarge (16 vCPU/128 GB) ×1–2 $736 + 512 GB EBS $41 ~$777 ~$1 554 (warm standby)
Graviton alt. r7g.2xlarge (8 vCPU/64 GB) $0.396/h → $289 + EBS ~$309 ~$619

Egress: $0.09/GB after 100 GB/mo free (internet). Managed Postgres (pgvector) via RDS ≈ 1.6–2.2× above EC2 self-host in exchange for operations.

5.4 Self-Hosted — GCP GCE (on-demand)

Engine Recommended Machine GCE + PD Single-Node Monthly HA Monthly
Qdrant OSS n2d-standard-16 (16 vCPU/64 GB) ×1–3 $0.608/h → $444 + 256 GB PD $29.7 ~$473 ~$947 (×2)
Milvus OSS 2× n2d-standard-16 + n2d-standard-8 $444×2 + $222 + PD $59 ~$790 ~$1 169
pgvector n2d-standard-16 ×1–2 $444 + 512 GB PD $59.4 ~$503 ~$1 006

SUD/CUD discounts: 20–55% (CUD 1y ≈ –37%, 3y ≈ –55%).

5.5 TCO Summary — Monthly Cost Comparison

Engine Managed (SaaS) Self-Host AWS Self-Host GCP Key TCO Insight
Qdrant ~$250–450 $388 / $777 $473 / $947 Lowest RAM/payload in benchmarks = lowest infra cost
Milvus ~$600 (cap-opt) — $1 993 (perf) $756 / $1 900 $790 / $1 169 Cheapest storage ($0.025/GB/mo) but multi-node footprint
pgvector ~$850–900 — $1 650 (HA) $777 / $1 554 $503 / $1 006 Zero additional licensing; reuse existing Postgres ops
Pinecone $1 274 @1M q — $12 290 @10M q n/a n/a Serverless = zero idle cost, but RU cost scales with index size

Key TCO insight: At 10M×1 536, a single query scans ~72 GB → Pinecone's serverless RU model prices reads at ~$0.0012/query. At 10M queries/mo that is ~$12K — an order of magnitude above dedicated-compute alternatives. Pinecone mitigates with Dedicated Read Nodes (DRNs); Zilliz/Qdrant/self-host charge fixed compute, not scans.

6. Hybrid Search Architecture Blueprint

6.1 Architecture Overview

The following Mermaid diagram illustrates a production hybrid search architecture supporting dense vector similarity + sparse lexical search (BM25) with Reciprocal Rank Fusion (RRF), applicable across all four engines.

graph TB
    subgraph "Client Layer"
        A[Application Backend] -->|"Query (text + filters)"| B[API Gateway / Load Balancer]
    end

    subgraph "Query Processing Layer"
        B --> C[Query Router]
        C -->|"Dense path"| D[Embedding Service<br/>OpenAI ada-002 / Cohere]
        C -->|"Sparse path"| E[Tokenization &<br/>Sparse Encoding]
        C -->|"Filter path"| F[Metadata Filter<br/>Parser]
    end

    subgraph "Vector Database Layer"
        D --> G[Dense Vector Index<br/>HNSW / IVF / DiskANN]
        E --> H[Sparse Inverted Index<br/>BM25 / SPLADE]
        F --> I[Metadata Filter Engine<br/>Scalar Indexes / B-tree]
        G --> J[Result Merger<br/>Reciprocal Rank Fusion]
        H --> J
        I --> J
    end

    subgraph "Re-Ranking & Post-Processing"
        J -->|"Top-K candidates"| K[Cross-Encoder Re-Ranker<br/>bge-reranker / Cohere]
        K --> L[Business Logic Filter<br/>& Deduplication]
    end

    subgraph "Response"
        L --> M[Ranked Results<br/>+ Metadata + Scores]
        M --> B
    end

    subgraph "Data Ingestion Pipeline"
        N[Document Store] -->|"Batch / Streaming"| O[Chunking &<br/>Embedding Service]
        O -->|"Dense vectors"| G
        O -->|"Sparse vectors"| H
        O -->|"Metadata"| I
        P[Change Data Capture<br/>Debezium / Kafka] -->|"Real-time updates"| O
    end

    subgraph "Observability"
        Q[Prometheus / Grafana] -.->|"Metrics"| G
        Q -.->|"Metrics"| H
        R[LangSmith / Langfuse] -.->|"Traces"| K
        S[Alert Manager] -.->|"Anomalies"| B
    end

6.2 Engine-Specific Hybrid Search Implementations

Qdrant: Native Hybrid (Recommended for Performance)

  # Qdrant collection with dense + sparse vectors
collections:
  documents:
    vectors:
      dense:
        size: 1536
        distance: Cosine
      sparse:
        size: 30522  # BERT vocab size for BM25-like sparse
        distance: Dot
    optimizers_config:
      indexing_threshold: 20000
    on_disk: true  # Enable mmap for large datasets
  # Qdrant hybrid search with RRF
from qdrant_client import QdrantClient
from qdrant_client.models import (
    Prefetch, Query, Fusion, Filter, FieldCondition, MatchValue
)

client = QdrantClient(host="localhost", port=6333)

results = client.query_points(
    collection_name="documents",
    prefetch=[
        Prefetch(
            query=dense_embedding,  # 1536-dim float vector
            using="dense",
            limit=20,
            filter=Filter(must=[FieldCondition(key="tenant_id", match=MatchValue(value="acme"))])
        ),
        Prefetch(
            query=sparse_embedding,  # sparse vector
            using="sparse",
            limit=20
        ),
    ],
    query=Fusion(fusion="rrf"),  # Reciprocal Rank Fusion
    limit=10
)

Milvus: Hybrid with Weighted Scoring

  # Milvus hybrid search with weighted ranking
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType

connections.connect(host="localhost", port="19530")

  # Collection schema with dense + sparse fields
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
    FieldSchema(name="dense_vector", dtype=DataType.FLOAT_VECTOR, dim=1536),
    FieldSchema(name="sparse_vector", dtype=DataType.SPARSE_FLOAT_VECTOR),
    FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=65535),
    FieldSchema(name="metadata", dtype=DataType.JSON),
]

  # Hybrid search request
search_params = {
    "data": [dense_embedding, sparse_embedding],
    "anns_field": ["dense_vector", "sparse_vector"],
    "param": [
        {"metric_type": "COSINE", "params": {"ef": 128}},
        {"metric_type": "IP", "params": {"drop_ratio_search": 0.2}},
    ],
    "limit": 10,
    "expr": 'metadata["tenant_id"] == "acme"',
    "rerank": {
        "strategy": "rrf",
        "params": {"k": 60}
    }
}

pgvector: Application-Level Hybrid via FTS + RRF

-- pgvector hybrid: combine pgvector ANN with PostgreSQL FTS using RRF
WITH dense_results AS (
    SELECT id, text, 1.0 / (1 + embedding <=> $1::vector) AS dense_score
    FROM documents
    WHERE tenant_id = 'acme'
    ORDER BY embedding <=> $1::vector
    LIMIT 50
),
sparse_results AS (
    SELECT id, text, ts_rank_cd(text_search, plainto_tsquery('english', $2)) AS sparse_score
    FROM documents
    WHERE tenant_id = 'acme'
      AND text_search @@ plainto_tsquery('english', $2)
    ORDER BY ts_rank_cd(text_search, plainto_tsquery('english', $2)) DESC
    LIMIT 50
),
combined AS (
    SELECT id, text,
           ROW_NUMBER() OVER (ORDER BY dense_score DESC) AS dense_rank,
           ROW_NUMBER() OVER (ORDER BY sparse_score DESC) AS sparse_rank
    FROM dense_results
    FULL OUTER JOIN sparse_results USING (id)
)
SELECT id, text,
       (1.0/60.0) / (1 + dense_rank) + (1.0/60.0) / (1 + sparse_rank) AS rrf_score
FROM combined
ORDER BY rrf_score DESC
LIMIT 10;

Pinecone: Native Hybrid Index

  # Pinecone hybrid index creation and query
import pinecone

pc = Pinecone(api_key="YOUR_API_KEY")

  # Create hybrid index
pc.create_index(
    name="documents",
    dimension=1536,
    metric="cosine",
    spec=ServerlessSpec(cloud="aws", region="us-east-1"),
)

index = pc.Index("documents")

  # Upsert with dense + sparse vectors
index.upsert(vectors=[
    {
        "id": "doc_1",
        "values": dense_embedding,  # 1536-dim
        "sparse_values": {"indices": sparse_indices, "values": sparse_values},
        "metadata": {"tenant_id": "acme", "text": "..."}
    }
])

  # Hybrid query
results = index.query(
    vector=dense_embedding,
    sparse_vector={"indices": sparse_indices, "values": sparse_values},
    top_k=10,
    filter={"tenant_id": {"$eq": "acme"}},
    include_metadata=True
)

6.3 Hybrid Search Architecture Decision Matrix

Capability Qdrant Milvus pgvector Pinecone
Dense + sparse in one collection ✅ Native ✅ Native ❌ App-level ✅ Native
Built-in RRF fusion Fusion(fusion="rrf") rerank={"strategy": "rrf"} ❌ SQL ROW_NUMBER() approach ✅ Built-in
Weighted scoring ✅ Custom fusion rerank={"strategy": "weighted"} ❌ App-level ❌ RRF only
Filter + hybrid jointly ✅ Native filterable HNSW ✅ Filter expressions ✅ WHERE clause ✅ Metadata filter
BM25 / full-text ✅ Built-in sparse vectors ✅ Sparse inverted (2.4.5+) ✅ PostgreSQL FTS (tsvector) ✅ Full-text index
Query latency (hybrid, est.) ~30–80 ms ~80–250 ms ~100–500 ms ~40–120 ms

7. Production Configuration Reference

7.1 Qdrant — Production Docker Compose

  # docker-compose.qdrant.yml — Production single-node
version: '3.8'
services:
  qdrant:
    image: qdrant/qdrant:v1.12.1
    ports:
      - "6333:6333"  # REST API
      - "6334:6334"  # gRPC
    volumes:
      - qdrant_data:/qdrant/storage
      - qdrant_config:/qdrant/config
    environment:
      QDRANT__SERVICE__GRPC_PORT: 6334
      QDRANT__STORAGE__OPTIMIZERS__INDEXING_THRESHOLD_KB: 20000
      QDRANT__STORAGE__MEMMAP_THRESHOLD_KB: 20000
      QDRANT__STORAGE__PERFORMANCE__MAX_SEARCH_THREADS: 0
    deploy:
      resources:
        limits:
          memory: 100G
        reservations:
          memory: 80G
    ulimits:
      memlock:
        soft: -1
        hard: -1
    restart: unless-stopped

volumes:
  qdrant_data:
    driver: local
  qdrant_config:
    driver: local

7.2 Qdrant — Cloud Deployment (Terraform)

  # main.tf — Qdrant Cloud cluster via Terraform
terraform {
  required_providers {
    qdrant = {
      source  = "qdrant/qdrant"
      version = "~> 1.0"
    }
  }
}

provider "qdrant" {
  api_key = var.qdrant_api_key
}

resource "qdrant_cluster" "production" {
  name   = "vector-prod-10m"
  cloud  = "aws"
  region = "us-east-1"

  cluster_config {
    node_type {
      cpu    = 8
      memory = 64
    }
    node_count = 3  # HA: RF=2

    disk {
      size_gb = 256
      type    = "gp3"
    }
  }

  # Enable hybrid search
  collection_defaults {
    optimizers_config {
      indexing_threshold = 20000
    }
  }
}

7.3 Milvus — Production Helm Values

  # values.milvus.yaml — Production distributed deployment
cluster:
  enabled: true

image:
  repository: milvusdb/milvus
  tag: v2.5.4

  # Query node (handles search)
queryNode:
  replicas: 2
  resources:
    requests:
      cpu: "8"
      memory: "64Gi"
    limits:
      cpu: "16"
      memory: "128Gi"
  persistence:
    enabled: true
    size: 256Gi
    storageClass: "gp3"

  # Data node (handles inserts)
dataNode:
  replicas: 2
  resources:
    requests:
      cpu: "4"
      memory: "32Gi"
    limits:
      cpu: "8"
      memory: "64Gi"

  # Index node (handles indexing)
indexNode:
  replicas: 1
  resources:
    requests:
      cpu: "8"
      memory: "64Gi"
    limits:
      cpu: "16"
      memory: "128Gi"

  # Coordinator
proxy:
  replicas: 2
  service:
    type: LoadBalancer

  # Storage
minio:
  enabled: false

externalS3:
  enabled: true
  host: "s3.us-east-1.amazonaws.com"
  port: 443
  bucketName: "milvus-production-data"
  useSSL: true

  # Monitoring
metrics:
  serviceMonitor:
    enabled: true
  grafana:
    enabled: true

7.4 pgvector — Production PostgreSQL Configuration

-- postgresql.conf additions for pgvector at 10M scale
-- Shared memory & buffers (for r6i.4xlarge: 128 GB RAM)
shared_buffers = '32GB'
effective_cache_size = '96GB'
maintenance_work_mem = '4GB'
work_mem = '256MB'

-- Parallel query
max_parallel_workers_per_gather = 4
max_parallel_workers = 8
parallel_tuple_cost = 0.01
parallel_setup_cost = 100

-- WAL & checkpoint
wal_buffers = '64MB'
checkpoint_completion_target = 0.9
max_wal_size = '10GB'

-- HNSW index parameters (for 10M vectors)
-- CREATE INDEX CONCURRENTLY required for zero-downtime indexing
-- Production index creation for 10M vectors
-- Step 1: Create HNSW index (concurrent, no lock)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_documents_embedding_hnsw
ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);

-- Step 2: Create metadata indexes
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_documents_tenant
ON documents (tenant_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_documents_created
ON documents (created_at DESC);

-- Step 3: Tune HNSW search parameters (session-level)
SET hnsw.ef_search = 128;  -- Higher = better recall, slower
-- SET hnsw.ef_search = 64;  -- Default; lower = faster, lower recall

-- Step 4: Verify recall against exact search
EXPLAIN ANALYZE
SELECT id, text, 1 - (embedding <=> $1::vector) AS similarity
FROM documents
WHERE tenant_id = 'acme'
ORDER BY embedding <=> $1::vector
LIMIT 10;

7.5 Pinecone — Production Configuration

  # pinecone_production.py — Production setup for 10M vectors
import pinecone
from pinecone import ServerlessSpec, PodSpec

pc = Pinecone(api_key="YOUR_API_KEY")

  # Serverless index (recommended for variable workloads)
pc.create_index(
    name="documents-prod",
    dimension=1536,
    metric="cosine",
    spec=ServerlessSpec(
        cloud="aws",
        region="us-east-1",
    ),
    deletion_protection="enabled",
)

  # For high-throughput production: Dedicated Pod spec
  # pc.create_index(
  #     name="documents-dedicated",
  #     dimension=1536,
  #     metric="cosine",
  #     spec=PodSpec(
  #         pod_type="p2.x8",      # 8 GB RAM per pod
  #         pods=3,                 # HA across AZs
  #         environment="us-east-1-aws",
  #         metadata_config={"indexed": ["tenant_id", "category"]},
  #     ),
  # )

index = pc.Index("documents-prod")

  # Configure index for hybrid search
  # (sparse vectors enabled automatically on creation with hybrid support)

  # Production upsert with metadata filtering support
import numpy as np

def batch_upsert(documents, batch_size=100):
    """Upsert 10M documents in batches."""
    for i in range(0, len(documents), batch_size):
        batch = documents[i:i+batch_size]
        vectors = []
        for doc in batch:
            vectors.append({
                "id": doc["id"],
                "values": doc["embedding"],       # 1536-dim
                "sparse_values": doc.get("sparse"), # optional sparse
                "metadata": {
                    "tenant_id": doc["tenant_id"],
                    "category": doc["category"],
                    "created_at": doc["created_at"],
                    # Keep metadata < 40 KB per vector
                }
            })
        index.upsert(vectors=vectors)

7.6 Monitoring Stack — Prometheus + Grafana

  # prometheus.yml — Scrape vector DB metrics
scrape_configs:
  - job_name: 'qdrant'
    static_configs:
      - targets: ['qdrant:6333']
    metrics_path: '/metrics'

  - job_name: 'milvus'
    static_configs:
      - targets: ['milvus-proxy:9091', 'milvus-querynode:9091']
    metrics_path: '/metrics'

  - job_name: 'pgvector'
    static_configs:
      - targets: ['postgres-exporter:9187']
    metrics_path: '/metrics'

  - job_name: 'pinecone'
    # Pinecone metrics via API or Datadog integration
    static_configs:
      - targets: ['pinecone-exporter:9090']
  # grafana/dashboard.json — Key alerts for vector DB operations
alerts:
  - name: HighQueryLatency
    condition: histogram_quantile(0.99, rate(vector_search_duration_seconds_bucket[5m])) > 0.1
    severity: critical
    message: "p99 search latency exceeds 100ms"

  - name: LowRecall
    condition: vector_recall_at_10 < 0.95
    severity: warning
    message: "Recall@10 below 0.95 threshold"

  - name: IndexBuildStalled
    condition: rate(vector_index_build_progress_total[10m]) == 0
    severity: warning
    message: "Index build progress stalled for 10 minutes"

  - name: HighMemoryUsage
    condition: vector_memory_usage_bytes / vector_memory_total_bytes > 0.9
    severity: critical
    message: "Memory usage exceeds 90%  risk of OOM or mmap thrashing"

  - name: IngestionQPSDrop
    condition: rate(vector_inserts_total[5m]) < expected_ingestion_rate * 0.5
    severity: warning
    message: "Ingestion rate dropped below 50% of expected"

8. Decision Framework: When to Choose What

8.1 Decision Tree

START: What is your primary constraint?
│
├─► "We already run PostgreSQL"
│   └─► pgvector
│       ├─ QPS need < 100? → pgvector is sufficient
│       ├─ Need ACID transactions on vectors + metadata? → pgvector (only option)
│       └─ QPS need > 100? → Consider Qdrant alongside Postgres (dual-write)
│
├─► "We need maximum QPS at lowest cost"
│   └─► Qdrant
│       ├─ Managed? → Qdrant Cloud ($250–450/mo)
│       ├─ Self-hosted? → Qdrant OSS on r6i.2xlarge ($388/mo)
│       └─ Need hybrid search? → Native dense+sparse RRF built-in
│
├─► "We need the richest index types (DiskANN, GPU, IVF variants)"
│   └─► Milvus
│       ├─ Managed? → Zilliz Cloud ($600/mo cap-optimized)
│       ├─ Self-hosted? → Milvus distributed on k8s ($756–1 900/mo)
│       └─ Budget-constrained? → Zilliz capacity tier (cheapest storage: $0.025/GB)
│
├─► "We need zero-ops and auto-scaling"
│   └─► Pinecone
│       ├─ Low query volume (< 1M/mo)? → Pinecone serverless (~$50–1 274)
│       ├─ High query volume (> 1M/mo)? → Evaluate DRNs or switch to Qdrant/Zilliz
│       └─ Need BYOC? → Pinecone Enterprise (min $500/mo)
│
└─► "We need hybrid search (dense + sparse)"
    ├─ Best performance? → Qdrant (native filterable HNSW + BM25)
    ├─ Best flexibility? → Milvus (weighted scoring + multiple fusion strategies)
    └─ Lowest ops? → Pinecone (managed hybrid index)

8.2 Workload-Specific Recommendations

Workload Recommended Engine Rationale
RAG (Retrieval-Augmented Generation) Qdrant or Pinecone Native hybrid search (dense + sparse) for better retrieval; Qdrant for cost, Pinecone for zero-ops
Semantic search (embeddings only) Qdrant Highest QPS/lowest latency at equal recall; lowest TCO
E-commerce product search (filters + text) Qdrant or Milvus Qdrant for filterable HNSW; Milvus if you need DiskANN for very large catalogs
Analytics dashboard (Postgres-native) pgvector Zero new infrastructure; full SQL; ACID guarantees
Real-time recommendation (high QPS, low latency) Qdrant Sub-10ms p99 at 1M scale; native filtering avoids post-filter latency penalty
Multi-tenant SaaS Qdrant or pgvector Qdrant: collection-per-tenant with filterable HNSW; pgvector: row-level security + RLS policies
Compliance-heavy (HIPAA, SOC2) pgvector (RDS) or Pinecone Enterprise pgvector inherits RDS compliance; Pinecone has HIPAA add-on
Cost-sensitive startup (< $500/mo budget) Qdrant Cloud or pgvector Qdrant Cloud starts ~$250/mo; pgvector on existing RDS = $0 incremental

8.3 Migration Pathways

From → To Complexity Key Steps
pgvector → Qdrant Medium Dual-write during migration; vector re-index in Qdrant; validate recall; cut over reads
Pinecone → Qdrant Medium Export via Pinecone API; bulk upsert to Qdrant; update SDK client
Milvus → Qdrant Low-Medium Export collections via milvus-backup; import to Qdrant; update SDK client
Any → pgvector Low pgvector accepts any vector format; INSERT ...::vector; create HNSW index
Qdrant → Milvus Low-Medium Snapshot export; Milvus bulk insert; map schema (Qdrant collections → Milvus collections)

9. Migration & Architecture Guidance

9.1 Dual-Write Pattern (Zero-Downtime Migration)

┌─────────────────────────────────────────────────────────────┐
│                    DUAL-WRITE PATTERN                       │
│                                                             │
│  Application ──┬──► Source DB (primary)                    │
│                │                                            │
│                └──► Target DB (shadow / new)                │
│                                                             │
│  Phase 1: Write to both, read from source                  │
│  Phase 2: Validate consistency + recall                    │
│  Phase 3: Switch reads to target, stop writes to source    │
│  Phase 4: Decommission source                              │
└─────────────────────────────────────────────────────────────┘

9.2 Production Architecture Patterns

Pattern A: Single Engine (Simplest)

App → Qdrant/Milvus/Pinecone → (vector search + metadata filter)

Best for: New projects, dedicated vector search workloads.

Pattern B: Polyglot Persistence (pgvector + Dedicated Engine)

App → PostgreSQL (pgvector) → ACID transactions, relational queries
App → Qdrant → High-QPS vector search, hybrid search

Best for: Teams with existing Postgres who need > 100 QPS vector search. Dual-write vectors to both; use pgvector for transactional queries, Qdrant for search.

Pattern C: Microservice with Dedicated Vector Service

App → API Gateway → Vector Search Service → Qdrant/Milvus
                   → Metadata Service → PostgreSQL/Redis
                   → Embedding Service → OpenAI/Cohere

Best for: Large teams, multi-service architectures, independent scaling of vector search vs metadata.

9.3 Performance Tuning Cheat Sheet

Parameter Qdrant Milvus pgvector Pinecone
HNSW m (connections) 16–64 (default 16) 16 (HNSW index) 16 (default) Managed
HNSW ef_construction 100–500 (default 100) 200 (HNSW index) 200 (recommended) Managed
HNSW ef_search Runtime param (128 recommended) Runtime param SET hnsw.ef_search = 128 Managed
Quantization SQ (default), BQ (aggressive), PQ (balanced) SQ8, fp16, SQ4 halfvec, binary quantize Managed BQ
Memory optimization mmap threshold (20K KB default) DiskANN for disk-based shared_buffers tuning Serverless (auto)
Max threads MAX_SEARCH_THREADS=0 (auto) Configurable per node max_parallel_workers Managed

10. Appendix: Sources & Caveats

10.1 Benchmark Sources

10.2 Pricing Sources

10.3 Data Caveats

  1. Measured vs estimated: §3.1 benchmarks are measured on identical hardware. §3.3 and §3.4 are extrapolated estimates — clearly labeled with ⚠️.
  2. pgvector community data: pgvector has no vendor-sponsored 10M×1 536 benchmark. Numbers come from Ann-benchmarks, community tests, and documented HNSW/IVFFlat semantics.
  3. Cloud prices: All prices are Sept 2026 list prices. Verify exact rates before publishing.
  4. Dimension scaling: 10M×96 measured data extrapolated to 1 536-dim using 4–16× cost multiplier.
  5. Ingestion pressure: QPS figures assume static (non-ingesting) state unless noted. Ingestion degrades QPS 1.1× (Qdrant) to 9× (Milvus).
  6. Recall tuning: Recall@10 is a tuning knob, not a fixed property. All engines allow trading recall for QPS via ef_search, nprobe, etc.
  7. Pinecone RU model: At 10M×1 536, each query scans ~72 GB = ~72 Read Units. This makes high-QPS workloads (10M+ queries/mo) an order of magnitude more expensive than fixed-compute alternatives.

Document version: 1.0 | Last updated: 2026-09-16 | Target audience: Enterprise technical buyers, platform engineers, ML/AI architects

💼 Enterprise AI Architecture & Cluster Deployment Advisory

Need help sizing, deploying, and hardening private LLM gateways, high-throughput vector search, or distributed agent clusters? We build production-ready, SOC-2 compliant private infrastructure.

High-Throughput Vector DB Blueprint 2026

Cloud GPU TCO & Token Throughput Benchmark 2026

Orch 2.0 Autonomous Multi-Agent Publishing Pipeline

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.