Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Vector Databases

Overview

A vector database stores, indexes, and searches embeddings — high-dimensional vectors produced by neural models (see Embeddings) — using approximate nearest neighbor (ANN) search. It is the retrieval backbone of production RAG systems (see RAG), semantic search, recommendation, and deduplication.

Vector search answers “which stored vectors are closest to this query vector?” — but exact search (scan all vectors, compute distance) is O(N·d) per query and unusable at scale. Vector databases build ANN indexes that trade a small, configurable amount of recall for orders-of-magnitude faster search.

How a Vector Database Works

graph TD
    DOC["Documents / images / audio"] --> EMB["Embedding model<br/>(e.g., text-embedding-3, BGE)"]
    EMB --> VEC["Vector (e.g., 1024–3072 dims)"]
    VEC --> IDX["ANN index<br/>(HNSW, IVF, ...)"]
    IDX --> STORE["Storage + metadata<br/>(payload: source, timestamp, access)"]
    QUERY["Query text"] --> QEMB["Embed query"]
    QEMB --> SEARCH["ANN search (cosine / dot / L2)"]
    SEARCH --> RANK["Top-k candidates<br/>+ optional metadata filters"]
    RANK --> LLM["Passed to LLM context (RAG)"]

The vector database adds three things a plain vector library (like FAISS) does not:

  1. Persistence and durability (library indexes live in RAM/process).
  2. Metadata/payload filtering (“same vector, but only category = X”).
  3. Operational features — replication, sharding, high availability, incremental upserts/deletes.

ANN Index Structures

HNSW (Hierarchical Navigable Small World) — the default choice

A multi-layer graph: upper layers have long links (fast coarse navigation), the bottom layer has fine-grained links to actual neighbors. Search walks down the layers toward the query.

graph TB
    L2["Layer 2 (few nodes, long links)"] --> L1["Layer 1 (more nodes)"]
    L1 --> L0["Layer 0 (all nodes, nearest neighbors)"]
    Q["Query entry point"] --> L2

Key parameters:

ParameterEffect
MMax links per node; higher = better recall, more memory
ef_constructionBuild-time search width; higher = better index quality, slower build
ef_searchQuery-time search width; higher = better recall, slower query

Memory is the main cost: a 768-dim float32 vector is ~3 KB of raw data, and HNSW graph edges multiply that.

IVF (Inverted File)

Clusters vectors with k-means into nlist partitions; query checks the nearest few partitions (nprobe), then does exact search inside them. IVF-PQ additionally applies product quantization to compress vectors (16–64× smaller), enabling billion-scale in RAM — at some recall cost.

FLAT / exact

Brute-force scan. Only for small datasets (< ~100K vectors) or when 100% recall is mandatory.

Distance Metrics

MetricUse case
CosineMost common for text embeddings (direction matters, magnitude irrelevant)
Dot productFast, assumes normalized vectors; default for many embedding models
L2 / EuclideanWhen absolute distance matters

The Ecosystem (as of 2026)

SystemTypeNotes
PineconeManaged SaaSZero ops; but no exposure of HNSW tuning knobs
MilvusOpen sourceBillion-scale, many index types (IVF, HNSW, DiskANN), horizontal scale-out
QdrantOpen source (Rust)Very fast filtered search, payload filtering first-class
WeaviateOpen sourceNative hybrid search (BM25 + dense), GraphQL, multimodal
pgvectorPostgres extensionHNSW (v0.5+) and IVFFlat; SQL filters “just work”; degrades past ~10M vectors without care; by far the cheapest option if you already run Postgres
ChromaOpen source, embeddedPrototyping and small apps
Elasticsearch / OpenSearchSearch engineHNSW + dense in existing search stack
VespaOpen sourceHybrid + structured ranking at scale
FAISS / ScaNNLibrary (not a DB)In-process, static datasets, experiments

Choosing: ≤ ~5M vectors and you already use Postgres → pgvector. Need sub-10 ms with heavy metadata filtering → Qdrant. Billion-scale or many index types → Milvus. Want hybrid search out of the box → Weaviate. Zero-ops managed → Pinecone.

Dense (embedding) search alone misses exact-keyword matches (“Paxos” spelled exactly, product codes). Hybrid search combines:

score = α · dense_similarity + (1 − α) · sparse_similarity (BM25)

with α tuned per domain, plus optional RRF (Reciprocal Rank Fusion) to merge result lists without tuning weights. Weaviate, Vespa, Qdrant, and OpenSearch/Elasticsearch support this natively; pgvector does not out of the box.

Practical Considerations

  • Chunking quality beats index choice — retrieval quality is dominated by how documents are chunked (see RAG).
  • Filtered search changes the index math — pushing a metadata filter inside HNSW (filtered graphs) behaves differently from post-filtering.
  • Quantization (PQ/SQ8) trades memory for recall; test at your recall target (e.g., recall@10 ≥ 0.95).
  • Staleness: embedding models change; re-embedding and re-indexing is an operational chore (version your embedding space).
  • Keep embeddings normalized if you use dot product; store the model + version metadata so queries and corpus match.

Interview Questions

Q: Vector database vs vector library (FAISS)?

A library gives you an in-memory index and search primitives; a database adds durability, metadata filtering, incremental upserts, replication, sharding, and operational tooling. Use a library for static/experimental data; a database for production RAG.

Q: Why approximate search instead of exact?

Exact kNN is O(N·d) per query — at 100M vectors that’s seconds. ANN indexes (HNSW/IVF) reduce latency to milliseconds while keeping recall at 95–99%+, which is what RAG needs. The trade-off is controlled via index parameters.

It builds a hierarchical graph: coarse layers with long-range links navigate to the right neighborhood quickly; the bottom layer contains exact neighbor links. Search is greedy graph traversal from a top-layer entry point, then descends — effectively log-like scaling instead of linear scan.

Q: When would you choose pgvector over a dedicated vector database?

When the dataset is modest (≤ ~5M vectors), you already run Postgres, and you value transactional consistency with relational data and SQL filtering. Dedicated systems win at extreme scale, specialized index types, and filtered-latency guarantees.

References

  • HNSW paper: Malkov & Yashunin, Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs (2016/2018) — https://arxiv.org/abs/1603.09320
  • Johnson, Douze, Jégou, Billion-scale similarity search with GPUs (FAISS) — https://arxiv.org/abs/1702.08734
  • pgvector documentation — https://github.com/pgvector/pgvector
  • Milvus — https://milvus.io/docs
  • Qdrant — https://qdrant.tech/documentation/
  • Weaviate — https://weaviate.io/developers/weaviate
  • ANN-Benchmarks — https://ann-benchmarks.com/
  • Embeddings — how vectors are produced
  • RAG — retrieval pipeline that consumes vector search
  • Indexing — classic database indexes (B-trees, hash) vs ANN
  • Distributed Storage — sharding and replication patterns