{"article":{"slug":"announcing-pg-vectorsearch-scalable-open-source-vector-search-in-postgresql","title":"Announcing pg_vectorsearch: Scalable Open-Source Vector Search in PostgreSQL","subtitle":null,"summary":"Tiger Data launches pg_vectorsearch, an open-source PostgreSQL extension whose PRISM index uses partitioning and compression instead of HNSW graphs to deliver faster vector queries with far less memory and quicker index builds.","content_type":"announcement","language":"en","canonical_url":"https://www.tigerdata.com/blog/announcing-pg_vectorsearch-scalable-open-source-vector-search-postgresql","author":{"name":"Erik Nordström, Matvey Arye, Noah Hein","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Tiger Data","url":"https://www.tigerdata.com/","listing_slug":null,"listing":null},"topics":[{"name":"Databases","slug":"databases","url":"https://listedarticles.com/topics/databases"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"},{"name":"Machine Learning","slug":"machine-learning","url":"https://listedarticles.com/topics/machine-learning"},{"name":"Infrastructure","slug":"infrastructure","url":"https://listedarticles.com/topics/infrastructure"},{"name":"Announcements","slug":"announcements","url":"https://listedarticles.com/topics/announcements"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":2613,"reading_minutes":11,"published_at":"2026-10-13T00:00:00.000Z","added_at":"2026-10-13T20:15:24.949Z","updated_at":"2026-10-13T20:15:24.949Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/announcing-pg-vectorsearch-scalable-open-source-vector-search-in-postgresql","markdown_url":"https://listedarticles.com/articles/announcing-pg-vectorsearch-scalable-open-source-vector-search-in-postgresql.md","example":false,"citation":"Erik Nordström, Matvey Arye, Noah Hein, Tiger Data. \"Announcing pg_vectorsearch: Scalable Open-Source Vector Search in PostgreSQL.\" 13 Oct 2026. https://www.tigerdata.com/blog/announcing-pg_vectorsearch-scalable-open-source-vector-search-postgresql (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://www.tigerdata.com/blog/announcing-pg_vectorsearch-scalable-open-source-vector-search-postgresql"},"body_markdown":"TLDR: We are launching pg_vectorsearch, a new open-source PostgreSQL extension that makes vector search dramatically faster and more cost-efficient. Its new PRISM index uses partitioning and compression instead of HNSW's graph-based approach, delivering faster queries with a fraction of the memory and significantly faster index builds. At 100 million vectors, PRISM achieved 6 ms query latency on a machine with 4× less memory than HNSW, with index builds over 20× faster.\n\nVector search is more useful when it lives alongside the rest of your data. For RAG, semantic search, and recommendations, keeping vectors in PostgreSQL means you can join results with application data, update both in a single transaction, and use the SQL tools you already know. Plus, you don’t need to operate separate database systems and sync data between them.\n\npgvector has made this possible, with HNSW becoming the go-to vector index in PostgreSQL. But HNSW is memory hungry. Searching its graph involves repeated random reads, so good performance depends on keeping much of that graph in memory. In our benchmarks at 100 million vectors, the smallest machine on which HNSW finished building had 512 GB of RAM. Those resource demands can push teams out of the PostgreSQL they love and into a dedicated vector database, which requires fewer resources due to newer, partition-based indexing algorithms but forces your application to use a separate database only for vector search.\n\nWe built pg_vectorsearch so you can stay in PostgreSQL. It's a new open-source extension, currently in alpha, whose Partitioned Routing Index for Similarity Matching (PRISM) index delivers faster vector search with fewer resources than pgvector's HNSW. It’s more resource-efficient for both small and large workloads.\n\nMeet Prism\n\nPRISM brings a partition-based approach to PostgreSQL, similar to the approach used by newer dedicated vector search systems. Instead of connecting every vector to its neighbors in a graph, PRISM groups similar vectors into partitions and stores them as a list in a compressed format that is densely packed into database pages. When searching, it identifies the most relevant partitions and scans through their vectors. This has two advantages: the index is much smaller because it stores compressed vectors without graph connections, and searching is more efficient because the database reads data sequentially rather than jumping between locations in memory (or disk). Together, these improvements let PRISM deliver fast vector search with much less RAM, all within PostgreSQL, where your data already lives.\n\nThe difference is substantial. At 100 million vectors, PRISM builds and serves the index on a 128 GB machine and delivers 6 ms p50 query latency at 95% recall, compared with HNSW's 8 ms on a machine with four times more memory (512 GB!). Building the index is similarly faster: 1 hour 40 minutes with PRISM on the 128 GB box, versus 42 hours with HNSW on the 512 GB server. The benefits extend to smaller deployments: at 1 million vectors and 99% recall, PRISM delivers 15 ms latency on a 2 GB machine, while HNSW takes 392 ms on a machine with twice the memory (more than 23x slower).\n\nScaling vector search shouldn't have to mean leaving PostgreSQL. And staying in PostgreSQL gives you more than just vector search. You can combine semantic similarity with BM25 keyword ranking using pg_textsearch, filter by time, metadata, or hierarchical relationships, and join results with the rest of your application data. All using SQL, in a single database, without syncing data between separate systems.\n\nBenchmarks\n\nEvery result in this post comes from our public benchmark tool. Recall is recall@10 against exact brute-force results. Both indexes ran with the same PostgreSQL configuration on the same instance types. All HNSW results use pgvector's 16-bit halfvec, which gave HNSW the best tradeoff between query performance and index size in our testing [1].\n\nAt 100 million vectors, PRISM runs on a machine HNSW can't even build on\n\nWe indexed 100 million Cohere embeddings on two AWS instances: an r8i.4xlarge with 16 vCPUs and 128 GiB of RAM, and an r8i.16xlarge with 64 vCPUs and 512 GiB, which costs four times as much per hour.\n\nOn the smaller machine, PRISM built the index in 1 hour 40 minutes and served queries at 99% recall in 21 ms. HNSW couldn't build there at all. On the larger machine, HNSW finished after 42 hours and served 95% recall in 8 ms, but we couldn't get it to 99% recall at a usable latency. PRISM built on the same large machine in 49 minutes and served 99% recall in 19 ms.\n\nInstance Size\n\nR8i.4xlarge (128 GB)\n\nR8i.16xlarge (512 GB)\n\nAlgorithm\n\nPRISM\n\nHNSW\n\nPRISM\n\nHNSW\n\nRelative hourly cost of that instance\n\n1x\n\n1x\n\n4x\n\n4x\n\nBuild time\n\n1 h 40 min\n\nDid not finish\n\n49 min\n\n42 hours\n\nIndex size\n\n25 GB\n\n180 GB\n\n25 GB\n\n180 GB\n\nP50 Latency for 95% recall@10\n\n6 ms\n\n-\n\n4 ms\n\n8 ms\n\nP50 Latency for 99% recall@10\n\n21 ms\n\n-\n\n19 ms\n\nnot achieved\n\nWe also tried to “cheat” to help HNSW out. If HNSW can't be built on the small machine, maybe it can be built on the big one and then served from the small one. We'd never recommend this in production, since resizing means downtime and you'd be serving an index on hardware that can't rebuild it. We tried it anyway to see whether the shortcut exists. It doesn't. On the r8i.4xlarge, the moved HNSW index took over a second per query at 95% recall and couldn't reach 99% at all. At 100 million vectors, HNSW needs the large machine for queries as well.\n\nAt 1 million vectors, PRISM serves 99% recall on 2 GiB of RAM\n\nMost teams don't have 100 million vectors, so we checked whether the pattern holds at a more common scale. We indexed 1 million vectors on a c8g.large with 4 GiB of RAM and a c8g.medium with 2 GiB.\n\nOn the c8g.large, PRISM built in about 3 minutes. HNSW took about 21.7 hours, because the graph doesn't fit in 4 GiB during construction and the build thrashes. Given that, we couldn’t realistically try HNSW on the smaller machine.\n\nInstance Size\n\nC8g.medium (2GB)\n\nc8g.large (4 GB)\n\n1M vectors\n\nPRISM\n\nHNSW\n\nPRISM\n\nHNSW\n\nBuild time\n\n~3 min\n\nDid not finish\n\n~3.1 min\n\n~21.7 h\n\nRelative hourly cost of that instance\n\n1x\n\n1x\n\n2x\n\n2x\n\n2Index size\n\n280 MB\n\n-\n\n280 MB\n\n2.05 GB\n\nP50 Latency for 95 % recall@10\n\n9 ms\n\n-\n\n3 ms\n\n122 ms\n\nP50 Latency for 99% recall@10\n\n15 ms\n\n-\n\n9 ms\n\n392 ms\n\nOn the same 4 GiB machine, the PRISM index is about seven times smaller than HNSW's and answers queries about 40 times faster at the same recall. On half that memory, PRISM still serves 99% recall in 15 ms, while HNSW needs 392 ms on the bigger box.\n\nWhy and how it works\n\nWhy we moved away from graph indexes\n\nThis isn’t the first vector index we’ve built and open-sourced for PostgreSQL. In 2024, Tiger Data releasedpgvectorscale, which used a graph-based indexing algorithm inspired by Microsoft’s DiskANN research. We deliberately chose a disk-optimized design so that indexes could scale beyond available RAM. Our initial benchmarks were promising, but as adoption grew and we tested against a wider variety of datasets, particularly low-dimensional ones, we encountered a troubling problem: portions of the graph could become unreachable, causing recall to degrade. We searched the literature for a principled solution but couldn’t find one, and the heuristics we tried worked on some datasets but not others.\n\nThat’s a frustrating failure mode for a search index. When queries are slow, you can measure the problem and tune accordingly. But when recall degrades, you might not even notice until users start questioning the quality of their search results. And telling them to experiment with yet another set of tuning parameters isn’t the experience we want to provide.\n\nMeanwhile, partitioned-list approaches reminiscent of IVFFlat were getting a second life, as advances in compression and optimization addressed many of their old limitations. More importantly, they offered stronger guarantees than we could achieve by patching a graph-based index. We came to believe they were a better foundation, and decided to start over.\n\nWith the introduction of pg_vectorsearch, we are shifting the majority of our vector-search development efforts to this new architecture. The existing pgvectorscale extension, which is stable and handling production workloads currently, will enter maintenance mode.\n\nScanning partitions beats walking a graph on modern CPUs\n\nGoogle’s ScaNN for AlloyDB, Microsoft’s SPANN, and turbopuffer’s ANN engine have converged on the same approach: partition the vector space, compress the vectors, and scan nearby partitions efficiently with SIMD. The rest of this section explains why the industry has largely moved off of graphs, towards partitions, and how PRISM takes these learnings and applies them to a native Postgres index.\n\nA big reason partitioned indexes can win comes down to how CPUs read memory. A graph search narrows the candidates quickly by hopping from node to linked node, but each hop's address comes from the node before it. The CPU can't prefetch an address it can't predict, so most hops miss the cache, and the core sits idle until memory answers. Faster distance math doesn't help much when most of the time goes to waiting. Once the graph spills to disk, those same hops become scattered reads. And every vector carries edges to its neighbors, which is part of why the graph is so large to begin with.\n\nA partitioned index splits the vector space into clusters with k-means. Each cluster stores compressed vector codes and row IDs back to back. A query walks a small tree of cluster centroids, which the Postgres buffer cache keeps warm, to find the clusters nearest the query, then scans every vector in them. That scan reads memory in a straight line, so the prefetcher stays ahead of the CPU, and SIMD loops score many vectors per instruction. The limit becomes memory bandwidth, of which modern hardware has plenty.\n\nNew compression algorithms make those scans even cheaper because they replace full vectors with much smaller codes. PRISM stores each vector as a RaBitQ code of roughly one bit per dimension, so scanning a partition means moving and scoring far less data. Because compressed distances are approximate, indexes typically rerank promising candidates against the full vectors, which requires random reads from the table. RaBitQ's error bounds rule out candidates that can't be in the top results, so far fewer need that second look.\n\nPartitioning also makes RaBitQ's error bounds tighter. Its error grows with the size of the vector it compresses, and a partitioned index compresses each vector's offset from its cluster's centroid, which is much smaller than the vector relative to the origin. HNSW has no centroid to subtract, so it can't get that benefit. It’s unusual but the index structure and compression reinforce each other and are not independent.\n\nPartition boundaries create another problem: a vector near the edge of one cluster may be relevant to queries routed to a neighboring one. Scanning more partitions helps, and storing boundary vectors in more than one partition helps more directly, trading a little storage for recall. ScaNN's SOAR algorithm does this by “spilling” a vector into a second cluster in a geometry-aware way. PRISM implements this spilling technique as well.\n\nGraphs still fit some workloads, especially small, fast-changing datasets. HNSW grows one vector at a time, while a partitioned index needs enough data to form meaningful clusters. That gap is narrowing as newer techniques let partitions split, merge, and rebalance as data changes, and it's where PRISM is headed next.\n\npg_vectorsearch today\n\nThis is the first public release of pg_vectorsearch, and we're calling it an alpha. The core works and you can test it on real data today, but there are gaps you should know about before you do.\n\nWhat works today:\n\nCreating an index. Index builds on a populated table, including parallel builds, and k-nearest-neighbor search with L2, inner product, and cosine distance.\n\npgvector compatibility. PRISM works with pgvector’s vector and halfvec data types. You can build a PRISM index directly on existing pgvector columns with no rewrites and no copies, and queries written with pgvector's operators work unchanged. But, pgvector is not a dependency: pg_vectorsearch ships its own vec32 and vec16 types so that it can operate independently.\n\nOptimized distance computation works across a wide range of architectures. SIMD distance optimizations for AVX2, AVX-512, and NEON.\n\nSimplified tuning. For queries, there is a single parameter to tune: prism.nprobe, the number of clusters a query scans. The default of 0 picks a value automatically to target roughly 95% recall.\n\nInserts and updates work now but heavy churn can cause recall drift without manual intervention. Right now, we don’t re-partition automatically under churn so the partitioning can become suboptimal. You can split oversized partitions manually by calling prism_rebalance or re-build the full index from scratch by calling REINDEX. We plan to remove the need for these manual steps in the future and handle drift automatically. For this version, we don't recommend creating the index on an empty table and filling it afterwards.\n\nDelete operations work but don’t yet reuse space. Deletes are turned into tombstones by VACUUM so that they can be skipped by scans, but we don’t yet reuse the space inside index pages. You can run REINDEX to reclaim space.\n\nPRISM is a native PostgreSQL index access method, so there's no separate service to run.\n\nPlanned for the GA release:\n\nAutomatic, incremental rebalancing. As mentioned above, currently you need to run manual steps to have the index repartition and rebalance under heavy churn. We plan to automate this.\n\nStreaming search, with WHERE clauses filters alongside ORDER BY, streamed so the index keeps producing candidates until the filter is satisfied.\n\nHardening: testing for physical replication, backup and restore, and multi-client throughput.\n\nFurther out: parallel query execution, multi-column indexes for multi-tenant tables, disk reclamation during VACUUM, and a billion-vector benchmark.\n\nCREATE EXTENSION pg_vectorsearch;\nCREATE TABLE documents (\nid bigserial PRIMARY KEY,\nbody text,\nembedding vec32(768)\n);\n-- Load your data first. PRISM sizes the index from the row count at build time.\nCREATE INDEX ON documents USING prism (embedding vec32_l2_ops);\nSELECT id, body\nFROM documents\nORDER BY embedding <-> $1\nLIMIT 10;\n\nIf you already have a pgvector column, you can build the PRISM index on it directly. The column isn't rewritten or copied, and your existing queries keep using pgvector's operators:\n\nCREATE EXTENSION pg_vectorsearch;\nCREATE INDEX ON documents USING prism (embedding vec32_l2_ops);\n\nUse vec32_cosine_ops or vec32_ip_ops for other distance functions, and the vec16_* operator classes for halfvec columns. The one setting most deployments touch is prism.nprobe, the number of partitions each query scans. The default of 0 picks a value automatically to target roughly 95% recall. Raise it for more recall at some cost in latency:\n\nSET prism.nprobe = 40;\n\nThe repo's tuning guide covers the rest, and its README walks through migrating from an existing HNSW index.\n\npg_vectorsearch is open source under the PostgreSQL License. The alpha is the best time to shape where it goes, so run it against your own data and tell us where it falls short on GitHub. If your results disagree with ours, open an issue and we'll dig in.\n\n[1]: pgvector's binary quantization shrinks HNSW further, but the index is still double the size of PRISM while recall drops sharply, and recovering it means reranking full vectors outside the index scan in a CTE. We couldn't get good recall at competitive speed that way, which matches results others have published.\n\nTimescaleDB 2.27: Broader Vectorized Execution, Up to 160x More Efficient UPDATE/DELETE, and Smarter UPSERT Pruning\n","body_html":"<p>TLDR: We are launching pg_vectorsearch, a new open-source PostgreSQL extension that makes vector search dramatically faster and more cost-efficient. Its new PRISM index uses partitioning and compression instead of HNSW&#39;s graph-based approach, delivering faster queries with a fraction of the memory and significantly faster index builds. At 100 million vectors, PRISM achieved 6 ms query latency on a machine with 4× less memory than HNSW, with index builds over 20× faster.</p>\n<p>Vector search is more useful when it lives alongside the rest of your data. For RAG, semantic search, and recommendations, keeping vectors in PostgreSQL means you can join results with application data, update both in a single transaction, and use the SQL tools you already know. Plus, you don’t need to operate separate database systems and sync data between them.</p>\n<p>pgvector has made this possible, with HNSW becoming the go-to vector index in PostgreSQL. But HNSW is memory hungry. Searching its graph involves repeated random reads, so good performance depends on keeping much of that graph in memory. In our benchmarks at 100 million vectors, the smallest machine on which HNSW finished building had 512 GB of RAM. Those resource demands can push teams out of the PostgreSQL they love and into a dedicated vector database, which requires fewer resources due to newer, partition-based indexing algorithms but forces your application to use a separate database only for vector search.</p>\n<p>We built pg_vectorsearch so you can stay in PostgreSQL. It&#39;s a new open-source extension, currently in alpha, whose Partitioned Routing Index for Similarity Matching (PRISM) index delivers faster vector search with fewer resources than pgvector&#39;s HNSW. It’s more resource-efficient for both small and large workloads.</p>\n<p>Meet Prism</p>\n<p>PRISM brings a partition-based approach to PostgreSQL, similar to the approach used by newer dedicated vector search systems. Instead of connecting every vector to its neighbors in a graph, PRISM groups similar vectors into partitions and stores them as a list in a compressed format that is densely packed into database pages. When searching, it identifies the most relevant partitions and scans through their vectors. This has two advantages: the index is much smaller because it stores compressed vectors without graph connections, and searching is more efficient because the database reads data sequentially rather than jumping between locations in memory (or disk). Together, these improvements let PRISM deliver fast vector search with much less RAM, all within PostgreSQL, where your data already lives.</p>\n<p>The difference is substantial. At 100 million vectors, PRISM builds and serves the index on a 128 GB machine and delivers 6 ms p50 query latency at 95% recall, compared with HNSW&#39;s 8 ms on a machine with four times more memory (512 GB!). Building the index is similarly faster: 1 hour 40 minutes with PRISM on the 128 GB box, versus 42 hours with HNSW on the 512 GB server. The benefits extend to smaller deployments: at 1 million vectors and 99% recall, PRISM delivers 15 ms latency on a 2 GB machine, while HNSW takes 392 ms on a machine with twice the memory (more than 23x slower).</p>\n<p>Scaling vector search shouldn&#39;t have to mean leaving PostgreSQL. And staying in PostgreSQL gives you more than just vector search. You can combine semantic similarity with BM25 keyword ranking using pg_textsearch, filter by time, metadata, or hierarchical relationships, and join results with the rest of your application data. All using SQL, in a single database, without syncing data between separate systems.</p>\n<p>Benchmarks</p>\n<p>Every result in this post comes from our public benchmark tool. Recall is recall@10 against exact brute-force results. Both indexes ran with the same PostgreSQL configuration on the same instance types. All HNSW results use pgvector&#39;s 16-bit halfvec, which gave HNSW the best tradeoff between query performance and index size in our testing [1].</p>\n<p>At 100 million vectors, PRISM runs on a machine HNSW can&#39;t even build on</p>\n<p>We indexed 100 million Cohere embeddings on two AWS instances: an r8i.4xlarge with 16 vCPUs and 128 GiB of RAM, and an r8i.16xlarge with 64 vCPUs and 512 GiB, which costs four times as much per hour.</p>\n<p>On the smaller machine, PRISM built the index in 1 hour 40 minutes and served queries at 99% recall in 21 ms. HNSW couldn&#39;t build there at all. On the larger machine, HNSW finished after 42 hours and served 95% recall in 8 ms, but we couldn&#39;t get it to 99% recall at a usable latency. PRISM built on the same large machine in 49 minutes and served 99% recall in 19 ms.</p>\n<p>Instance Size</p>\n<p>R8i.4xlarge (128 GB)</p>\n<p>R8i.16xlarge (512 GB)</p>\n<p>Algorithm</p>\n<p>PRISM</p>\n<p>HNSW</p>\n<p>PRISM</p>\n<p>HNSW</p>\n<p>Relative hourly cost of that instance</p>\n<p>1x</p>\n<p>1x</p>\n<p>4x</p>\n<p>4x</p>\n<p>Build time</p>\n<p>1 h 40 min</p>\n<p>Did not finish</p>\n<p>49 min</p>\n<p>42 hours</p>\n<p>Index size</p>\n<p>25 GB</p>\n<p>180 GB</p>\n<p>25 GB</p>\n<p>180 GB</p>\n<p>P50 Latency for 95% recall@10</p>\n<p>6 ms</p>\n<p>-</p>\n<p>4 ms</p>\n<p>8 ms</p>\n<p>P50 Latency for 99% recall@10</p>\n<p>21 ms</p>\n<p>-</p>\n<p>19 ms</p>\n<p>not achieved</p>\n<p>We also tried to “cheat” to help HNSW out. If HNSW can&#39;t be built on the small machine, maybe it can be built on the big one and then served from the small one. We&#39;d never recommend this in production, since resizing means downtime and you&#39;d be serving an index on hardware that can&#39;t rebuild it. We tried it anyway to see whether the shortcut exists. It doesn&#39;t. On the r8i.4xlarge, the moved HNSW index took over a second per query at 95% recall and couldn&#39;t reach 99% at all. At 100 million vectors, HNSW needs the large machine for queries as well.</p>\n<p>At 1 million vectors, PRISM serves 99% recall on 2 GiB of RAM</p>\n<p>Most teams don&#39;t have 100 million vectors, so we checked whether the pattern holds at a more common scale. We indexed 1 million vectors on a c8g.large with 4 GiB of RAM and a c8g.medium with 2 GiB.</p>\n<p>On the c8g.large, PRISM built in about 3 minutes. HNSW took about 21.7 hours, because the graph doesn&#39;t fit in 4 GiB during construction and the build thrashes. Given that, we couldn’t realistically try HNSW on the smaller machine.</p>\n<p>Instance Size</p>\n<p>C8g.medium (2GB)</p>\n<p>c8g.large (4 GB)</p>\n<p>1M vectors</p>\n<p>PRISM</p>\n<p>HNSW</p>\n<p>PRISM</p>\n<p>HNSW</p>\n<p>Build time</p>\n<p>~3 min</p>\n<p>Did not finish</p>\n<p>~3.1 min</p>\n<p>~21.7 h</p>\n<p>Relative hourly cost of that instance</p>\n<p>1x</p>\n<p>1x</p>\n<p>2x</p>\n<p>2x</p>\n<p>2Index size</p>\n<p>280 MB</p>\n<p>-</p>\n<p>280 MB</p>\n<p>2.05 GB</p>\n<p>P50 Latency for 95 % recall@10</p>\n<p>9 ms</p>\n<p>-</p>\n<p>3 ms</p>\n<p>122 ms</p>\n<p>P50 Latency for 99% recall@10</p>\n<p>15 ms</p>\n<p>-</p>\n<p>9 ms</p>\n<p>392 ms</p>\n<p>On the same 4 GiB machine, the PRISM index is about seven times smaller than HNSW&#39;s and answers queries about 40 times faster at the same recall. On half that memory, PRISM still serves 99% recall in 15 ms, while HNSW needs 392 ms on the bigger box.</p>\n<p>Why and how it works</p>\n<p>Why we moved away from graph indexes</p>\n<p>This isn’t the first vector index we’ve built and open-sourced for PostgreSQL. In 2024, Tiger Data releasedpgvectorscale, which used a graph-based indexing algorithm inspired by Microsoft’s DiskANN research. We deliberately chose a disk-optimized design so that indexes could scale beyond available RAM. Our initial benchmarks were promising, but as adoption grew and we tested against a wider variety of datasets, particularly low-dimensional ones, we encountered a troubling problem: portions of the graph could become unreachable, causing recall to degrade. We searched the literature for a principled solution but couldn’t find one, and the heuristics we tried worked on some datasets but not others.</p>\n<p>That’s a frustrating failure mode for a search index. When queries are slow, you can measure the problem and tune accordingly. But when recall degrades, you might not even notice until users start questioning the quality of their search results. And telling them to experiment with yet another set of tuning parameters isn’t the experience we want to provide.</p>\n<p>Meanwhile, partitioned-list approaches reminiscent of IVFFlat were getting a second life, as advances in compression and optimization addressed many of their old limitations. More importantly, they offered stronger guarantees than we could achieve by patching a graph-based index. We came to believe they were a better foundation, and decided to start over.</p>\n<p>With the introduction of pg_vectorsearch, we are shifting the majority of our vector-search development efforts to this new architecture. The existing pgvectorscale extension, which is stable and handling production workloads currently, will enter maintenance mode.</p>\n<p>Scanning partitions beats walking a graph on modern CPUs</p>\n<p>Google’s ScaNN for AlloyDB, Microsoft’s SPANN, and turbopuffer’s ANN engine have converged on the same approach: partition the vector space, compress the vectors, and scan nearby partitions efficiently with SIMD. The rest of this section explains why the industry has largely moved off of graphs, towards partitions, and how PRISM takes these learnings and applies them to a native Postgres index.</p>\n<p>A big reason partitioned indexes can win comes down to how CPUs read memory. A graph search narrows the candidates quickly by hopping from node to linked node, but each hop&#39;s address comes from the node before it. The CPU can&#39;t prefetch an address it can&#39;t predict, so most hops miss the cache, and the core sits idle until memory answers. Faster distance math doesn&#39;t help much when most of the time goes to waiting. Once the graph spills to disk, those same hops become scattered reads. And every vector carries edges to its neighbors, which is part of why the graph is so large to begin with.</p>\n<p>A partitioned index splits the vector space into clusters with k-means. Each cluster stores compressed vector codes and row IDs back to back. A query walks a small tree of cluster centroids, which the Postgres buffer cache keeps warm, to find the clusters nearest the query, then scans every vector in them. That scan reads memory in a straight line, so the prefetcher stays ahead of the CPU, and SIMD loops score many vectors per instruction. The limit becomes memory bandwidth, of which modern hardware has plenty.</p>\n<p>New compression algorithms make those scans even cheaper because they replace full vectors with much smaller codes. PRISM stores each vector as a RaBitQ code of roughly one bit per dimension, so scanning a partition means moving and scoring far less data. Because compressed distances are approximate, indexes typically rerank promising candidates against the full vectors, which requires random reads from the table. RaBitQ&#39;s error bounds rule out candidates that can&#39;t be in the top results, so far fewer need that second look.</p>\n<p>Partitioning also makes RaBitQ&#39;s error bounds tighter. Its error grows with the size of the vector it compresses, and a partitioned index compresses each vector&#39;s offset from its cluster&#39;s centroid, which is much smaller than the vector relative to the origin. HNSW has no centroid to subtract, so it can&#39;t get that benefit. It’s unusual but the index structure and compression reinforce each other and are not independent.</p>\n<p>Partition boundaries create another problem: a vector near the edge of one cluster may be relevant to queries routed to a neighboring one. Scanning more partitions helps, and storing boundary vectors in more than one partition helps more directly, trading a little storage for recall. ScaNN&#39;s SOAR algorithm does this by “spilling” a vector into a second cluster in a geometry-aware way. PRISM implements this spilling technique as well.</p>\n<p>Graphs still fit some workloads, especially small, fast-changing datasets. HNSW grows one vector at a time, while a partitioned index needs enough data to form meaningful clusters. That gap is narrowing as newer techniques let partitions split, merge, and rebalance as data changes, and it&#39;s where PRISM is headed next.</p>\n<p>pg_vectorsearch today</p>\n<p>This is the first public release of pg_vectorsearch, and we&#39;re calling it an alpha. The core works and you can test it on real data today, but there are gaps you should know about before you do.</p>\n<p>What works today:</p>\n<p>Creating an index. Index builds on a populated table, including parallel builds, and k-nearest-neighbor search with L2, inner product, and cosine distance.</p>\n<p>pgvector compatibility. PRISM works with pgvector’s vector and halfvec data types. You can build a PRISM index directly on existing pgvector columns with no rewrites and no copies, and queries written with pgvector&#39;s operators work unchanged. But, pgvector is not a dependency: pg_vectorsearch ships its own vec32 and vec16 types so that it can operate independently.</p>\n<p>Optimized distance computation works across a wide range of architectures. SIMD distance optimizations for AVX2, AVX-512, and NEON.</p>\n<p>Simplified tuning. For queries, there is a single parameter to tune: prism.nprobe, the number of clusters a query scans. The default of 0 picks a value automatically to target roughly 95% recall.</p>\n<p>Inserts and updates work now but heavy churn can cause recall drift without manual intervention. Right now, we don’t re-partition automatically under churn so the partitioning can become suboptimal. You can split oversized partitions manually by calling prism_rebalance or re-build the full index from scratch by calling REINDEX. We plan to remove the need for these manual steps in the future and handle drift automatically. For this version, we don&#39;t recommend creating the index on an empty table and filling it afterwards.</p>\n<p>Delete operations work but don’t yet reuse space. Deletes are turned into tombstones by VACUUM so that they can be skipped by scans, but we don’t yet reuse the space inside index pages. You can run REINDEX to reclaim space.</p>\n<p>PRISM is a native PostgreSQL index access method, so there&#39;s no separate service to run.</p>\n<p>Planned for the GA release:</p>\n<p>Automatic, incremental rebalancing. As mentioned above, currently you need to run manual steps to have the index repartition and rebalance under heavy churn. We plan to automate this.</p>\n<p>Streaming search, with WHERE clauses filters alongside ORDER BY, streamed so the index keeps producing candidates until the filter is satisfied.</p>\n<p>Hardening: testing for physical replication, backup and restore, and multi-client throughput.</p>\n<p>Further out: parallel query execution, multi-column indexes for multi-tenant tables, disk reclamation during VACUUM, and a billion-vector benchmark.</p>\n<p>CREATE EXTENSION pg_vectorsearch;\nCREATE TABLE documents (\nid bigserial PRIMARY KEY,\nbody text,\nembedding vec32(768)\n);\n-- Load your data first. PRISM sizes the index from the row count at build time.\nCREATE INDEX ON documents USING prism (embedding vec32_l2_ops);\nSELECT id, body\nFROM documents\nORDER BY embedding &lt;-&gt; $1\nLIMIT 10;</p>\n<p>If you already have a pgvector column, you can build the PRISM index on it directly. The column isn&#39;t rewritten or copied, and your existing queries keep using pgvector&#39;s operators:</p>\n<p>CREATE EXTENSION pg_vectorsearch;\nCREATE INDEX ON documents USING prism (embedding vec32_l2_ops);</p>\n<p>Use vec32_cosine_ops or vec32_ip_ops for other distance functions, and the vec16_* operator classes for halfvec columns. The one setting most deployments touch is prism.nprobe, the number of partitions each query scans. The default of 0 picks a value automatically to target roughly 95% recall. Raise it for more recall at some cost in latency:</p>\n<p>SET prism.nprobe = 40;</p>\n<p>The repo&#39;s tuning guide covers the rest, and its README walks through migrating from an existing HNSW index.</p>\n<p>pg_vectorsearch is open source under the PostgreSQL License. The alpha is the best time to shape where it goes, so run it against your own data and tell us where it falls short on GitHub. If your results disagree with ours, open an issue and we&#39;ll dig in.</p>\n<p>[1]: pgvector&#39;s binary quantization shrinks HNSW further, but the index is still double the size of PRISM while recall drops sharply, and recovering it means reranking full vectors outside the index scan in a CTE. We couldn&#39;t get good recall at competitive speed that way, which matches results others have published.</p>\n<p>TimescaleDB 2.27: Broader Vectorized Execution, Up to 160x More Efficient UPDATE/DELETE, and Smarter UPSERT Pruning</p>","headings":[]}}