The phrase "vector database" makes it sound like a category of product you go shopping for. On AWS it's usually a decision about which service already in your stack you point at the problem, and the cost differences between the options are large, unintuitive, and driven almost entirely by one number: how much of your index has to live in RAM. Get the sizing math right and the "which vector database" question mostly answers itself. Here's the math, the three options, and the honest answer about when you don't need any of them.
The index type decides everything downstream
Before comparing services, understand the three index families, because your choice sets the memory, recall, and latency profile no matter which product wraps it.
- Flat (brute force / exact). Compare the query to every vector. Perfect recall, zero index overhead, linear search time. Fine for tens of thousands of vectors, hopeless at millions. Memory is just the raw vectors.
- IVF (inverted file). Cluster the vectors into, say, a few thousand centroids at build time; at query time, search only the handful of clusters nearest the query. Trades a little recall for a large speedup, and the vectors can stay on disk. Memory-frugal, but recall depends on how many clusters you probe, and it needs enough data to cluster well.
- HNSW (hierarchical navigable small world). A layered graph you greedily walk from coarse to fine. It's the recall/latency champion — often the default in modern systems — but it's a graph of pointers that only performs when it's resident in RAM. This is the line item that surprises people.
Why HNSW is RAM-hungry
HNSW stores, per vector, both the vector itself and a list of graph edges to its neighbors on each layer. A useful rule of thumb for the total in-memory footprint:
bytes_per_vector ~= dimensions x 4 + M x 2 x 4 x layer_factor
# with 1,536 dims and M=16 (typical):
# vectors: 1,536 x 4 = 6,144 bytes
# graph: ~16 x 2 x 4 x 1.1 = ~140 bytes per layer, a few layers
# → call it ~6.5-7 KB per vector all-in
So ten million 1,536-dimension vectors is roughly 65-70 GB that must sit in memory for the index to be fast. That is the whole story of vector-database pricing. IVF lets much of that spill to disk; HNSW does not. When a managed vector service quotes you a price, you are mostly paying for enough RAM to hold an HNSW graph, and the way to cut the bill is to shrink the vectors (int8 quantization drops the vector portion 4x; Matryoshka truncation cuts the dimension count) rather than to shop for a cheaper logo.
Option 1: pgvector on RDS or Aurora Postgres
pgvector adds a vector column type and both IVFFlat and HNSW indexes to Postgres. The appeal is enormous and underrated: your vectors live in the same transactional database as the rows they describe, so you filter on tenant_id, join to metadata, and enforce consistency in one query, with one backup story and one thing to operate.
The cost is an RDS or Aurora instance, and the sizing question is the RAM question above. For a few million vectors with an HNSW index, you want an instance whose memory comfortably exceeds the index size — think an r6g/r7g memory-optimized class where a couple hundred gigabytes of RAM runs you in the low-thousands of dollars a month (region- and reservation-dependent; on-demand and Reserved differ substantially). The failure mode is quiet: let the HNSW index exceed available memory and Postgres pages it from disk, and your millisecond queries become hundred-millisecond queries with no error to tell you why. Size the instance to the index, not the other way around.
Option 2: OpenSearch k-NN
OpenSearch has a mature k-NN plugin (HNSW via the nmslib, faiss, or Lucene engines) and gives you what Postgres doesn't: real distributed sharding, so an index too big for one node spreads across a cluster automatically. If you're already running OpenSearch for logs or full-text search, adding vectors is incremental, and you get first-class hybrid retrieval — BM25 and k-NN in one query — for free.
You pay for it in operational weight and a per-node memory reservation the plugin enforces (by default it caps the k-NN graph at a fraction of node RAM). A production cluster is several data nodes plus dedicated masters; the bill starts higher than a single Postgres instance and climbs with shard count. OpenSearch earns that premium when you've genuinely outgrown a single machine or when you want lexical and vector search fused in the engine rather than in your application code.
Option 3: a dedicated vector database
Pinecone, Weaviate, Qdrant, Milvus — purpose-built engines, some available as AWS Marketplace offerings or self-hosted on EC2/EKS. They give you the sharpest recall/latency tuning, features like built-in quantization and metadata filtering designed for vectors, and serverless tiers that decouple storage from compute. If vector search is your product and you're operating at tens or hundreds of millions of vectors with tight latency SLAs, this is where the specialized tooling pays off.
The tradeoff is another system to run, another data-sync pipeline to keep consistent with your source of truth, and — for the managed serverless options — a pricing model based on stored vectors plus query volume that can surprise you as traffic grows. It's the right call at scale and premature almost everywhere else.
How many millions before you shard?
A rough progression, assuming 1,536-dimension vectors and an HNSW index:
- Up to ~1 million: a single mid-size Postgres instance with pgvector handles this comfortably in memory. Anything more elaborate is over-engineering.
- 1-10 million: still single-node territory on a memory-optimized instance, but now you're deliberately sizing RAM to the ~7 KB/vector figure and probably reaching for int8 quantization to keep it there.
- 10-50 million: the index (65-350 GB) starts straining the largest sensible single instances. This is where OpenSearch's sharding or a dedicated engine's horizontal scaling stops being optional.
- 50 million and up: distributed by necessity. Quantization isn't a nice-to-have; it's what keeps the cluster affordable.
Note how far you can push a single node with quantization. The same 10 million vectors at 1 byte/dimension int8 instead of 4-byte fp32 is ~16 GB instead of ~65 GB — the difference between a modest instance and a cluster.
When plain Postgres is plenty (and a managed vector DB is overkill)
For a lot of real applications — internal document search, a support knowledge base, a product catalog with a few hundred thousand items — you have well under a million vectors, modest query volume, and no sub-10-millisecond latency requirement. At that scale a plain Postgres b-tree for your metadata plus a pgvector column on the same table is not a compromise; it's the correct architecture. You avoid a second datastore, a sync pipeline, and a monthly bill for capacity you won't use, and you keep the ability to do a filtered vector search ("nearest neighbors where tenant_id = ?") in a single statement, which is exactly the query most applications actually run.
Reach for OpenSearch when you need sharding or engine-level hybrid search, and for a dedicated vector database when vector search is the core of the product and the scale demands it. Everywhere below those thresholds, the cost-optimal answer is the database you already have, sized so the index fits in RAM. The whole discipline of vector-database cost control comes down to that last clause.