Vector Databases

Purpose-built storage for embeddings — similarity search, metadata filtering, and indexing at scale

Why

A normal relational database is built to look up exact matches or ranges efficiently (WHERE id = 5, WHERE price BETWEEN 10 AND 20). It has no efficient built-in way to answer "which of these million vectors is mathematically closest to this new vector?" — that requires specialized similarity-search algorithms, which is exactly what a vector database is purpose-built for.

Explanation
Why not just use a regular database
Traditional DB --> Server --> RDBMS --> Tables --> Records + Fields Vector DB --> Server --> Collection --> Vectors --> Records (vector + metadata)
What a vector database stores and offers

For each entry, it stores the vector (the embedding), the metadata (source, page number, tags, timestamps), and an index — a data structure that keeps similarity search fast even across millions of vectors. It also supports full CRUD: Create (add new vectors), Read (search/retrieve), Update (re-embed and replace a vector by id if the source changed), Delete (remove a vector by id if the document is gone).

Similarity metrics — how "closeness" is measured
  • Cosine similarity — the angle between two vectors, ignoring magnitude. Most common for text embeddings, since most embedding models are trained to be compared this way.
  • Dot product — similar to cosine but also factors in vector magnitude; slightly faster to compute, and equivalent to cosine once vectors are normalized to length 1 (which most embedding models do automatically — see Embeddings for the formula).
  • Euclidean (L2) distance — straight-line distance between two points in space; more common for image/computer-vision embeddings, less common for text.

The metric isn't a free choice — it must match what the embedding model was trained and optimized against (documented per model), or similarity scores stop meaning what you think they mean.

Vector indexing — staying fast at scale

Comparing a query against every single stored vector (brute-force / flat search) is accurate but doesn't scale. Vector databases instead use Approximate Nearest Neighbor (ANN) algorithms, trading a small amount of accuracy for a large speedup.

Flat / brute-force
Compares the query to literally every vector. Exact results — fine for small datasets (under ~10k vectors), too slow beyond that.
IVF (Inverted File Index)
Clusters vectors ahead of time with k-means, each group with a centroid (average vector). A query compares against the centroids first, then searches only inside the nearest cluster(s) — not the whole collection.
HNSW (Hierarchical Navigable Small World)
Builds a multi-layered graph, navigating from coarse, well-spread layers down to dense, local ones to zero in on nearest neighbors. The default choice in most production vector databases (FAISS, ChromaDB, pgvector, Pinecone) — the best speed/accuracy trade-off overall.
PQ (Product Quantization)
Compresses each vector into a smaller, quantized code to save memory. Used alongside IVF or HNSW at very large scale, where memory — not compute — is the bottleneck.

What is k-means clustering, in plain terms? It groups similar vectors into k clusters: place K centroids at random, assign every vector to its nearest centroid, recompute each centroid as the average of the vectors assigned to it, and repeat until the centroids stop moving much. A million vectors end up organized into, say, 100 meaningful clusters, so a search only has to check the few clusters near the query instead of everything.

1. Cluster all vectors into k groups (k-means) 2. Each group gets a centroid — the average vector of that group 3. Query time: compare the query vector to the k centroids only 4. Search inside the nearest cluster(s), not the whole collection
HNSW's tuning knobs, and why they trade accuracy for speed

HNSW builds a graph where each vector is a node, connected to a small set of nearby neighbors, organized into layers — sparse, long-range connections at the top layer for fast coarse navigation, dense, short-range connections at the bottom layer for fine-grained accuracy. A search enters at the top layer and greedily walks toward the query, dropping down a layer each time it can't get closer, until it reaches the bottom layer's most precise neighborhood. Three parameters control the accuracy/speed/memory trade-off directly:

  • M — how many neighbor connections each node keeps. Higher M means better recall and more memory per vector.
  • ef_construction — how thoroughly the graph is built at index time. Higher values build a better graph but take longer to index.
  • ef_search — how many candidates are explored at query time. Higher values improve recall at the cost of query latency — this is the one knob you can safely tune per-query without rebuilding the index.

There's also LSH (Locality-Sensitive Hashing), an older ANN approach that hashes similar vectors into the same "bucket" with high probability, then only compares within matching buckets — largely superseded by HNSW in modern vector databases for text embeddings, but still relevant in some high-dimensional, memory-constrained settings.

Metadata filtering

Search results can be filtered by metadata before or alongside the similarity search — for example, only searching within documents tagged domain: "insurance".

vectorstore.similarity_search( query, k=5, filter={"domain": "insurance"} )

Useful for multi-tenant systems (only search a specific customer's data), access control (only search documents the user is allowed to see), or simply narrowing the search space for efficiency and precision. Filtering can happen before the ANN search (pre-filter, exact but can be slow if the filter is very selective against a huge index) or after it (post-filter, fast but can under-return results if the top-k happens to contain few matches for the filter) — which strategy a database uses by default is worth checking, since it changes correctness under a narrow filter.

Scaling out: sharding and replication

A single-node index eventually hits a ceiling on either memory (all vectors must typically stay resident for HNSW to be fast) or query throughput. Production vector databases address this the same way any distributed database does: sharding splits the vector collection across multiple nodes (each holding a subset of vectors, with a query fanning out to all shards and merging results), and replication duplicates the index across nodes for both read throughput and fault tolerance. This is invisible at small scale, but becomes the central operational concern once an index passes tens of millions of vectors or needs to serve high query-per-second traffic reliably.

Common vector database options
FAISS
A library, not a full server — runs in-process, extremely fast, free. Great for smaller or static datasets, but you manage persistence, sharding, and serialization yourself.
ChromaDB
A lightweight vector database with built-in persistence and metadata filtering. Easy to set up — good for prototyping and small-to-medium production use.
pgvector
A Postgres extension that lets you combine normal SQL filtering with vector similarity search in one query — a strong fit when your data already lives in Postgres, and one fewer system to operate.
Qdrant / Weaviate / Milvus
Open-source, self-hostable or managed vector databases built for production with filtering, sharding, and hybrid search built in — a common choice when you want production features without full vendor lock-in.
Pinecone
Fully managed, cloud-hosted, built for large-scale, high-concurrency production with minimal operational overhead — the trade-off is cost at scale and less control than self-hosting.
Elasticsearch / OpenSearch / Redis
General-purpose search/data platforms that have added vector search (kNN) capabilities — a practical choice when the team already operates one of these and doesn't want a dedicated vector store, at some cost in vector-search-specific performance versus a purpose-built option.
Cost considerations

Vector database cost scales primarily with two things: total vectors stored (drives memory/storage cost — HNSW indexes typically want to stay in RAM for full speed) and query volume (drives compute cost). A managed service bills for both directly; a self-hosted option shifts the cost to infrastructure and the engineering time to operate it. Dimension count and index parameters (like HNSW's M) both directly multiply memory usage, so the model and index choices made in Embeddings and above aren't independent of the cost of the database that stores them.