On this page · 9 sections
Summary. The number everyone quotes at you is 11.4x. In an April 2025 benchmark, Tiger Data measured Postgres hitting 471.57 queries per second against Qdrant's 41.47 at 99% recall over 50 million 768-dimension Cohere embeddings, on an AWS r6id.4xlarge that cost $835 a month at publication. Three facts get dropped when that number is repeated. It was run by the vendor selling the Postgres option, who state their bias in the post. It tested Postgres with pgvector and pgvectorscale, a separate extension most teams never install, not plain pgvector. And in their own words, "The queries did not involve filtering." That last one matters more than the throughput, because filtering is exactly where pgvector documents its sharpest edge: with the default hnsw.ef_search of 40, a filter matching 10% of rows returns about 4 rows. On the same test Qdrant won every latency percentile, and built the index in 3.3 hours against 11.1. Neither product is the loser here. The benchmark just does not answer the question most teams are actually asking.
Pick on your access pattern, not on someone else's QPS chart.
The three limits that actually decide this
Before any benchmark, check whether pgvector can represent your problem. Three documented limits eliminate more candidates than performance ever does.
Dimensions. pgvector stores vectors up to 16,000 dimensions but only indexes vector up to 2,000. Those are different numbers and the gap is where teams get hurt: your column will accept a 3,072-dimension embedding and then refuse to index it. The README's escape routes are halfvec for up to 4,000 dimensions, binary quantization for up to 64,000, subvector indexing, or dimensionality reduction. If your embedding model emits 3,072 dimensions and you are unwilling to quantize or truncate, plain pgvector indexing is out at the first hurdle.
Filtering. This is the one that bites in production RAG, where almost every query is scoped to a tenant, a document set, or a date range. pgvector's README is blunt: "With approximate indexes, filtering is applied after the index is scanned. If a condition matches 10% of rows, with HNSW and the default hnsw.ef_search of 40, only 4 rows will match on average." Ask for 10 results, get 4. The index did its job; the filter ate the answer afterwards.
Since 0.8.0 there is a real fix, iterative index scans, which "will automatically scan more of the index until enough results are found (or it reaches hnsw.max_scan_tuples or ivfflat.max_probes)". You choose ordering:
SET hnsw.iterative_scan = strict_order; -- exact distance order
SET hnsw.iterative_scan = relaxed_order; -- slightly out of order, better recall
SET hnsw.max_scan_tuples = 20000; -- 20,000 by default
Qdrant's answer is payload indexing, designed to make filtering fast inside a vector-first engine rather than applying it after the scan. That is the architectural difference, and no throughput chart run without filters will show it to you.
Ops. Everything else is a question about your team.
What the benchmark actually measured
The Tiger Data post is a good benchmark. It is transparent about methodology, which is precisely why it can be read against its own headline.
| Metric, 50M vectors, 768 dims, 99% recall | Postgres + pgvector + pgvectorscale | Qdrant 1.13.4 |
|---|---|---|
| Throughput | 471.57 QPS | 41.47 QPS |
| p50 latency | 31.07 ms | 30.75 ms |
| p95 latency | 60.42 ms | 36.73 ms |
| p99 latency | 74.60 ms | 38.71 ms |
| Index build time | ~11.1 hours | ~3.3 hours |
At 90% recall the shape holds: Postgres takes throughput 1,589.79 QPS to 360.81, and Qdrant takes every latency percentile, 4.74 ms against 9.54 ms at p50.
So Postgres wins throughput by an order of magnitude and loses tail latency and build time. Both stay under 100 ms at 99% recall. If your workload is a high-volume batch retrieval pipeline, throughput is your metric and Postgres looks excellent. If you are serving an interactive search box where p99 is the number your users feel, Qdrant's 38.71 ms against 74.60 ms is the honest read.
Two caveats the citing blogs consistently drop.
The setup ran Postgres 16.8, pgvector 0.6.1 and pgvectorscale 0.7.0, in April 2025. pgvector has since reached 0.8.3. Any post presenting these figures as "2026 benchmarks" is recycling a year-old run.
And pgvectorscale is not pgvector. It is a separate Timescale extension adding StreamingDiskANN, a disk-based index. The benchmark's winning configuration used the StreamingDiskANN index with binary quantization on, not pgvector's HNSW. If you CREATE EXTENSION vector on RDS and expect 471 QPS at 50M vectors, you have installed different software from the one that was measured.
To their credit, the authors say the quiet part out loud. Avthar Sewrathan and Matvey Arye of Tiger Data, who wrote the benchmark, put it this way: "We believe that your default choice should be a general-purpose database (and we are, of course, biased towards Postgres) unless there is a compelling reason to switch to a specialized database."
That is a fair position, honestly labelled. It is still a vendor's position.
They are also candid about the Qdrant side: "we had trouble finding the right parameters for Qdrant's HNSW. The defaults weren't great, and it was time-prohibitive to test all the possibilities used by ANN-Benchmark on such a big dataset. We iterated for weeks." A benchmark where one side was tuned by its own engineers and the other by people learning it is a benchmark with a thumb somewhere near the scale, disclosed or not.
Decide by scale
| Corpus size | Reasonable default | Why |
|---|---|---|
| Under 1M vectors | pgvector, HNSW | Exact search is often fast enough; one database to operate; joins and filters in plain SQL |
| 1M to 10M | pgvector, HNSW, tuned | Build time and maintenance_work_mem become real work, not blockers |
| 10M to 100M, unfiltered, throughput-led | pgvector plus pgvectorscale, or a dedicated store | This is the band the 50M benchmark speaks to |
| 10M to 100M, heavily filtered or tail-latency-led | Dedicated vector database | Post-filter recall collapse and p99 are pgvector's documented weak spots |
| Over 100M | Dedicated, sharded | Postgres scales up before it scales out; Qdrant uses sharding as its foundation for parallel search |
The honest boundary is not a vector count. It is whichever of these you hit first: an embedding wider than 2,000 dimensions that you will not quantize, filtered queries that need real recall, a p99 budget under about 40 ms, or a corpus past what one large machine holds.
Making pgvector actually work
If you stay on Postgres, most of the wins are configuration, and they are documented.
Build the index after loading data, and give it memory. From the README: "Indexes build significantly faster when the graph fits into maintenance_work_mem". Postgres tells you when it does not:
NOTICE: hnsw graph no longer fits into maintenance_work_mem after 100000 tuples
DETAIL: Building will take significantly more time.
HINT: Increase maintenance_work_mem to speed up builds.
SET maintenance_work_mem = '8GB';
SET max_parallel_maintenance_workers = 7; -- 2 by default, plus leader
SET max_parallel_workers = 8; -- 8 by default
Know your index knobs. HNSW defaults to m = 16 and ef_construction = 64, and searches with hnsw.ef_search = 40. "A higher value provides better recall at the cost of speed."
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);
SET hnsw.ef_search = 100;
IVFFlat is the cheaper build. The README's guidance is specific: create it after the table has data, choose lists as "rows / 1000 for up to 1M rows and sqrt(rows) for over 1M rows", and set probes to about sqrt(lists). ivfflat.probes defaults to 1, which is close to useless for recall.
Shrink the working set before you buy hardware. The README's scaling advice, in order: use halfvec instead of vector for tables, then binary quantization for indexes with re-ranking for search.
CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);
SELECT * FROM (
SELECT * FROM items ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]') LIMIT 20
) ORDER BY embedding <=> '[1,-2,3]' LIMIT 5;
That pattern, a cheap wide scan re-ranked exactly, is how you keep an index in memory at scale.
Measure recall rather than assuming it. Approximate search has no error message for being wrong.
BEGIN;
SET LOCAL enable_indexscan = off; -- exact search, compare against your approximate results
The engineering judgement, having built these: teams reach for a second database to fix a recall problem that was a hnsw.ef_search of 40 and an unfiltered assumption. Tune first. Migrate if tuning runs out.
What you give up by adding a database
The case for Postgres is not performance. It is that a filter is a WHERE clause, and a join is a join.
SELECT product_name, description, embedding <=> $1 AS distance
FROM products
WHERE category = 'electronics' AND in_stock = true
ORDER BY distance
LIMIT 10;
Doing that across a dedicated vector store and a relational database means two systems, two consistency stories, and application code stitching IDs together. Postgres also brings write-ahead logging, replication, point-in-time recovery, EXPLAIN (ANALYZE, BUFFERS), and pg_stat_statements. A dedicated store gives you native sharding, index-level filtering, and tail latency.
pgvector's ceiling is generous: a non-partitioned table has a 32 TB limit in Postgres by default, and partitioned tables can hold thousands of such partitions. Postgres will not run out of room before your operations team runs out of patience.
India-specific considerations
Two points for teams building from India.
The instance is the cost, not the licence. Both pgvector and pgvectorscale are open source under the Postgres License, and Qdrant is open source too, so the spend is hardware. The benchmark's r6id.4xlarge, at 16 vCPUs and 128 GB RAM, ran $835 a month in April 2025. Adding a dedicated vector service to a stack that already runs Postgres often means a second fleet of that shape, plus the people to operate it. For a mid-size Indian product team, consolidation on Postgres is frequently the right call for reasons that have nothing to do with QPS. Our FinOps notes on AI cloud spend cover the wider pattern.
The second is tenancy under the Digital Personal Data Protection Act 2023. Most Indian B2B RAG deployments filter by tenant on every query, which puts them squarely in pgvector's documented weak spot. If a post-filter quietly drops results, that is a quality bug. If a missing filter returns another tenant's chunks, that is a data protection incident. Enable iterative scans, and test the filtered path specifically. See our RAG knowledge assistant work and notes on evals catching silent failures.
FAQ
How eCorpIT can help
eCorpIT is a CMMI Level 5 certified technology organisation in Gurugram, and our senior engineering teams build production RAG systems on Postgres and on dedicated vector stores. We benchmark your actual corpus and your actual filters rather than a public dataset with filtering switched off, tune pgvector before recommending you buy anything, and migrate you to a dedicated store only when a documented limit says you should. If you are picking a vector store or fighting recall in one you already run, talk to us at /contact-us/.
References
- pgvector, pgvector/pgvector GitHub README (v0.8.3; dimension limits, index options, iterative scans, filtering behaviour)
- Avthar Sewrathan, Matvey Arye and Smitty, Tiger Data, Pgvector vs. Qdrant, 29 April 2025
- pgvector, CHANGELOG
- PostgreSQL, pgvector 0.8.2 released
- Timescale, pgvectorscale GitHub repository
- ANN-Benchmarks, ann-benchmarks.com
- Subramanya et al., StreamingDiskANN paper, arXiv:2105.09613
- Timescale, pgvector vs Pinecone benchmark
- Qdrant, Qdrant GitHub repository
Last updated: 16 July 2026.