← BlogBlog

Vector databases for production RAG: Pinecone, Weaviate, Qdrant, Chroma and pgvector at 500K records

Almost every vector database comparison measures recall on a clean public dataset, and none of that predicts what a production RAG system does at 500K records with a metadata filter on every query. The decisions that hurt later are less photogenic: how filtered search behaves when the filter is narrow, whether an exact part number can be found at all, how much of the index has to sit in RAM, and what changing the embedding model costs you. Here is how Pinecone, Weaviate, Qdrant, Chroma (ChromaDB) and pgvector differ on those, with every vendor number linked.

5Engines compared
500K+Records in the base we run
6.1 GBRAM for 500K 1,536-dim vectors
$0.33Pinecone storage per GB/month

Benchmarks measure the one thing that rarely fails

Public ANN benchmarks push a fixed corpus of uniform vectors through unfiltered top-k queries and report recall against queries per second. That is genuinely useful if you write index code. It is close to useless for picking what to run, because unfiltered recall at 500K records is the part that already works. The mainstream engines sit within a couple of points of each other there, and a reranker in front of the model erases whatever difference is left.

What you do notice is everything the benchmark left out. A filter that matches two percent of the corpus comes back with four results instead of twenty. A part number goes missing, because dense vectors do not do exact tokens. The p99 climbs to several times the median the moment three people query at once, and then one Tuesday a re-embedding run takes the index down for a night nobody put in the calendar.

One separation is worth making before any of this. Answer quality is mostly upstream of the database, in chunking, ranking and grounding, and we took that apart in what actually breaks in a RAG knowledge base at 500K records. This article is about the layer underneath: which store the vectors live in, and what that choice does to you in month six.

Dense vectors cannot find a part number

Ask a pure embedding index for contract SLA-2024-118 and it will hand you three contracts that read like it. The failure is structural rather than a tuning problem: identifiers carry meaning in their characters, not in their semantics, and that is what lexical search is for. Any base where people search by SKU, error code, invoice number or policy ID needs BM25 or sparse vectors sitting next to the embeddings, and the engines are not equal here.

Weaviate treats it as a first-class query type. Its hybrid search exposes an alpha parameter where 0 is pure keyword and 1 is pure vector, with Relative Score Fusion as the default fusion method since v1.24. That is one call and one number to tune, which is about as low-friction as this gets.

Qdrant assembles it from parts, and the parts are good. Hybrid queries combine sparse and dense vectors through a prefetch stage, fused with Reciprocal Rank Fusion or, since v1.11, Distribution-Based Score Fusion, which normalises the score distributions before combining rather than working off positions. Prefetches nest, so a multi-stage retrieve-then-rerank pipeline is expressible in a single request.

Pinecone supports sparse vectors too, with a documented ceiling of 2,048 non-zero values per sparse vector in its database limits. pgvector has no lexical search of its own, and does not need one, because it lives in Postgres: tsvector full-text search is already in the same database, and you fuse the two rankings in SQL. That is more code than Weaviate's alpha, and it is also the version you can debug with EXPLAIN. Chroma's where_document substring match is a filter, not a ranking, so it is not a substitute.

For what it is worth, the base we run does dense and BM25 together, then reranks the merged set. The reranker is the expensive part of that sentence, and it is still the cheapest fix we know for results that are on topic and useless.

The invoice is mostly a RAM invoice

HNSW is a graph you walk, and walking it from disk is not a thing you want to do. Every self-hosted engine therefore has the same underlying economics, and Chroma documents them the most bluntly of the five. Its single-node performance guide says the index must reside in system RAM, that anything under 2 GB is not recommended, and that once a collection exceeds available memory the operating system starts swapping and the system quickly becomes unusable. It even gives you the sizing rule: maximum collection size in millions equals available RAM in gigabytes times 0.245. Queries parallelise up to the number of vCPUs and then queue, which is where the p99 goes.

Weaviate states the same relationship as a planning rule in its resource planning docs: memory usage is roughly two times the memory footprint of all vectors. That doubling is the part teams forget when they size a box off the raw embedding volume.

What 500K embeddings at 1,536 dimensions ask of your memoryArithmetic on Weaviate's 2x rule of thumb and Qdrant's documented compression ratios, for 500K x 1,536-dim vectors (retrieved Aug 10, 2026)
Raw float32 vectors4 bytes per dimension, no index
3.1 GB
Plus HNSW, at Weaviate's 2x rule of thumbwhat you actually provision
6.1 GB
Scalar quantization, 4xvector payload only, graph is extra
0.8 GB
Binary quantization, 32xvector payload only, needs rescoring
0.1 GB

Compression is the lever that moves this, and Qdrant documents the trade honestly in its quantization guide: scalar quantization gives 4x with an error usually under 1%, binary quantization reaches up to 32x and up to a 40x speedup but wants high-dimensional centred data plus a rescoring pass, and product quantization goes to 64x while being slower, because the distance maths loses its SIMD path. Weaviate points at product quantization for the same reason. None of this is free accuracy, and all of it is cheaper than another 32 GB of RAM.

Managed changes the shape of the cost rather than the amount. On Pinecone's published pricing the Standard plan starts at a $50 monthly minimum, storage runs $0.33 per GB per month, writes are $4 to $4.50 per million write units and reads are $16 to $18 per million read units depending on cloud and region, with egress at $0.10 per GB. You never provision memory. You do get a bill that grows with query volume, which is the opposite exposure from a self-hosted box: one scales with how much you store, the other with how much you ask. A support assistant answering 50,000 questions a month and an internal base answering 500 have very different answers here, and it is the same calculation we walk through for what an AI system costs to build and run.

Plan for the day you change the embedding model

Every corpus gets re-embedded eventually. A better model ships, the dimension count changes, chunking gets fixed, a language is added. That is not a migration you run with an ALTER, it is a full rebuild of the index, and the engines differ more on that day than on any recall chart.

pgvector is the most transparent about the pain, because the pain is yours. Its README is direct that HNSW gives better query performance than IVFFlat but has slower build times and uses more memory, recommends setting maintenance_work_mem to something like 8 GB, and warns with an explicit notice when the graph no longer fits, wording it as no longer fitting into maintenance_work_mem after 100,000 tuples. Dimension caps matter here too: the vector type indexes up to 2,000 dimensions and halfvec up to 4,000, so a 3,072-dimension model forces a decision about half precision before you start rather than after.

Managed services move the work but bill it. Rebuilding on Pinecone means re-upserting the corpus, which is write units at $4 to $4.50 per million on Standard, plus $0.25 per GB if you go through import. That is cheap for one rebuild, and worth knowing before you have done four.

The pattern that survives contact with production is the same everywhere. Build the new index beside the old one under a different collection or namespace, dual-write during the overlap, run your evaluation set against both, then flip an alias. What differs between engines is how cheap that overlap is: for a few hours you are paying for two copies of everything, which is exactly the moment the RAM arithmetic above stops being theoretical.

What we would actually run: Pinecone, Weaviate, Qdrant, Chroma or pgvector

There is no winner, which is the unsatisfying but correct answer. There are workloads, and each of these engines is the right call for one of them. The column that matters most in the table below is the last one, because the trade you accept is the thing you will live with.

Our default pick per workload, and the trade it comes with
WorkloadWhat we would runWhyWhat you are accepting
Under a few million chunks, data already in PostgrespgvectorOne database, one backup, one access model; filters and joins live in the same query as the vectorsTuning scan limits, and assembling lexical search yourself
Heavy metadata filtering, self-hosted, data staying in your own infrastructureQdrantThe filterable index and the quantization options are built for narrow filters over large corporaYou operate it: payload indexes, snapshots, upgrades
Hybrid search wanted without building itWeaviateHybrid with alpha and score fusion is a first-class query, and ACORN handles filters by defaultMore concepts to learn, and memory planning still lands on you
Small team, no ops budget, spiky trafficPineconeNothing to provision or keep alive; capacity is somebody else's problemPer-read costs that grow with usage, and limits you cannot change
Prototype, laptop, single machineChromaThe shortest path from notebook to working retrievalA memory ceiling you will meet, and a migration afterwards

Our bias is on the record and worth weighing: we default to pgvector, and the 500K-record system runs on PostgreSQL with pgvector alongside Python, LangChain and FastAPI. The database has not been the constraint at that size. Retrieval quality has, every time. That is the general lesson rather than a recommendation to copy our stack: a second service is real operational weight, so it should be bought with a specific constraint, not with a feeling that the serious option must be the specialised one. The same argument, at the level of whole systems rather than indexes, is the build versus buy question.

Five tests to run before you commit to anything

None of this needs a vendor call or a proof of concept quarter. Each test below targets one of the failure modes above, and a bad result points at a fix instead of a mood. Run them on your own corpus, not on a sample, because the sample is what makes every engine look identical.

Load ten times what you have. Duplicate your corpus with perturbed vectors if you have to. You are not measuring recall, you are watching what memory does and where build time goes non-linear. If the index no longer fits the box at 10x, you just learned your real planning horizon.

Query with your narrowest real filter. Pick the one that matches under one percent of the corpus, a single tenant on a single document type in a date range, then check how many results come back and whether the right one is among them. Empty or short result sets here are the single most common production surprise.

Search for something by its identifier. Take a part number, contract reference or policy ID that appears exactly once and ask for it. Dense-only setups will return topical neighbours with high confidence scores, which is how this failure hides for months.

Measure p95 and p99 with concurrent load, never the median. Chroma's own docs describe queries parallelising up to the vCPU count and then queueing into linear latency growth, and every engine has a version of that curve. The median under one client tells you nothing about the Monday morning when everyone opens the assistant at the same time.

Rehearse the reindex. Rebuild the index from scratch with a different embedding model and time it end to end, including the double-storage window. Do this while nothing depends on it, because the alternative is doing it for the first time under pressure with a deprecation deadline.

Two afternoons of that will tell you more than any comparison article, including this one, because it runs against your filters and your identifiers. If the results point at retrieval quality rather than the store, the write-up on chunking and ranking at scale is the more useful next read, and if the answers need live system state rather than documents, that is a different problem again: connecting an agent to your data through tools instead of an index.

We build and operate these systems, so weigh that accordingly. The five tests cost you nothing and no vendor. If you would rather have a second pair of eyes on which constraint is going to bite a specific base first, a short audit is a low-key way to get one.

FAQ

Which is the best vector database in 2026?

There is no single answer, and any article that gives you one is selling something. The engines are close enough on unfiltered recall that the decision is made elsewhere: how filtered search behaves under narrow filters, whether lexical search is built in or assembled, whether cost grows with what you store or with how often you query, and how much operational work you can absorb. Pick against your workload, and start from what you already run rather than from a feature matrix.

At what point does pgvector stop being enough?

Later than most people assume. The constraints that eventually force a move are specific rather than general: sustained high query concurrency where Postgres connection handling becomes the bottleneck, filters so narrow that post-scan filtering keeps missing results even with iterative index scans, embedding dimensions above what the index types support, or a corpus large enough that build time and memory make routine reindexing impractical. If none of those are true today, a second database is infrastructure you operate for no answer-quality gain.

Is Chroma good enough for production RAG?

It depends entirely on corpus size, and Chroma (ChromaDB) is unusually honest about the limit. Its own single-node performance documentation states the HNSW index must live in system RAM, that under 2 GB of RAM is not recommended, and that once a collection exceeds available memory the system starts swapping and becomes unusable rather than merely slow. Their sizing rule puts maximum collection size in millions at available RAM in gigabytes times 0.245. Below that ceiling it works; the risk is that growth crosses it without warning.

Does switching vector database mean re-embedding everything?

Not necessarily. Embeddings are just float arrays, so if you keep the source chunks and the vectors, most migrations are an export and a bulk load. What actually gets re-done is the index build, plus any engine-specific structures like payload indexes or sparse representations. The migration that does force full re-embedding is a change of embedding model, which is a different event and worth keeping separate in your planning, since the two are often bundled into one scary project when they do not have to be.

Changelog
  • 10 August 2026Published.
Free process audit

See what this would look like in your operations.

Get in touch

30 minutes · we map your 3 best automation opportunities · no obligation