惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
WordPress大学
WordPress大学
T
Threatpost
K
Kaspersky official blog
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
T
Tenable Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LINUX DO - 热门话题
Scott Helme
Scott Helme
云风的 BLOG
云风的 BLOG
博客园 - Franky
Webroot Blog
Webroot Blog
D
DataBreaches.Net
Stack Overflow Blog
Stack Overflow Blog
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
F
Fortinet All Blogs
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
Google DeepMind News
Google DeepMind News
月光博客
月光博客
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Help Net Security
Help Net Security
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
O
OpenAI News
Martin Fowler
Martin Fowler
W
WeLiveSecurity
C
Cyber Attacks, Cyber Crime and Cyber Security
Project Zero
Project Zero
V
Vulnerabilities – Threatpost
L
LangChain Blog
P
Privacy & Cybersecurity Law Blog
S
Security Affairs
P
Privacy International News Feed
C
Cybersecurity and Infrastructure Security Agency CISA
MyScale Blog
MyScale Blog
NISL@THU
NISL@THU
T
Troy Hunt's Blog
C
Cisco Blogs
IT之家
IT之家

ByteByteGo Newsletter

MCP vs A2A vs ACP: How AI Agents Actually Talk to Each Other A Guide to Multi-Tenancy: Benefits and Challenges AI Customer Support at Scale: The Travel Industry’s $Billion Bet How LLMs Learn to Be Helpful (RLHF vs DPO) How Microsoft Ships AI Agents at Enterprise Scale EP221: How Docker Works Under the Hood LAST CALL FOR ENROLLMENT: Become an AI Engineer - Cohort 7 Streaming vs Batch: Two Philosophies of Data Processing The Agent Loop: How AI Goes From Answering Questions to Doing Things ChatGPT vs Gemini vs Claude: How They Differ LAST CALL FOR ENROLLMENT: Become an AI Engineer - Cohort 7 Proof of Human: How to Verify a Person Is Real and Unique Multi-Region Architecture: Going Global Without Going Broke How OpenAI Delivers Low-Latency Voice AI for 900M Users Inside Thinking Machines’ Interaction Models How AI Agents Manage Memory and Avoid Forgetfulness EP220: RAG vs Graph RAG vs Agentic RAG Top Anti-Patterns to Avoid in Service Architecture Large Language Models vs Small Language Models An Ex-Meta L8’s Agentic Engineering Setup AI-Native Leaders: The Organizational Playbook for Engineering Transformation at Scale EP219: 12 Open-source LLMs Observability for Beginners: Logs, Metrics, Traces, and Everything Around Them LAST CALL FOR ENROLLMENT: Build with Claude Code - Cohort 2 How Open-Weight Models Changed the AI Landscape A Guide to AI Inference Engineering EP218: The Typical AI Agent Stack, Explained Must- Know Deployment Strategies: From Big-Bang to Progressive Delivery Love Teaching? ByteByteGo Is Hiring Part-Time AI & Engineering Instructors What Salesforce Learned from 20,000 Enterprise Agent Deployments Token Spend Out of Control? The Case for Smarter Routing EP217: Latency vs Throughput vs Bandwidth The Path of a Request: A Tour of Modern Web Architecture How OpenAI Built Its Data Agent A Practical Guide to Becoming an AI-Native Engineer How DoorDash Built a Testing System to Evaluate LLMs Must-Know Failure Modes in Distributed Systems How Airtable Built the Search Layer Behind Their AI Features How Vercel Cut Build Wait Times From 90 Seconds To 5 EP216: RAGs vs Agents 🚀 New cohort based course launch: Build with Claude Code A Guide to Async Patterns in API Design How Netflix is Using Multimodal AI to Power Video Search How Snapchat Serves a Billion Predictions Per Second How Grab is Using AI Agents to Boost Team Productivity EP215: The Anatomy of an AI Agent LAST CALL FOR ENROLLMENT: Become an AI Engineer - Cohort 6 A Guide To Event-Driven Architectural Patterns High Performance Rate Limiting at Databricks How Figma Upgraded Data Pipeline from Multi-Day Latency to Real-Time How Pinterest Built a Production MCP Ecosystem EP214: Claude Code vs. OpenClaw: 5 Design Dimensions Become an AI Engineer | Enrollment Ends Soon Container Design Patterns for Distributed Systems How Instacart Built a Search for Billions of Products Connecting LLMs to the Real World: Tool Use, Function Calling, and MCP EP213: MCP vs Skills, Clearly Explained A Beginner’s Guide to Kubernetes The Tech Stack Powering Wise How Stripe Detects Fraudulent Transactions Within 100 ms How Amazon Uses LLMs to Recommend Products EP212: Data Warehouse vs Data Lake vs Data Mesh B-Trees vs LSM Trees: Comparison and Trade-Offs How DoorDash Launches a New Country in One Week The Security Architecture of GitHub Agentic Workflow EP211: How the JVM Works A Guide to Relational Database Design Figma Design to Code, Code to Design: Clearly Explained How LinkedIn Feed Uses LLMs to Serve 1.3 Billion Users EP210: Monolithic vs Microservices vs Serverless Must-Know Cross-Cutting Concerns in API Development How Spotify Ships to 675 Million Users Every Week Without Breaking Things Nextdoor’s Database Evolution: A Scaling Ladder A Guide to Context Engineering for LLMs EP209: 12 Claude Code Features Every Engineer Should Know Our New Book on Behavioral Interviews Is Now Available on Amazon Database Performance Strategies and Their Hidden Costs How Datadog Redefined Data Replication How Meta Turned Debugging Into a Product How Roblox Uses AI to Translate 16 Languages in 100 Milliseconds EP208: Load Balancer vs API Gateway LAST CALL FOR ENROLLMENT: Become an AI Engineer - Cohort 5 How to Implement API Security How Anthropic’s Claude Thinks How Netflix Live Streams to 100 Million Devices in 60 Seconds How Agentic RAG Works? Last Chance to Enroll | Become an AI Engineer | Cohort-Based Course EP207: Top 12 GitHub AI Repositories Event Sourcing Explained: Benefits and Use Cases How OpenAI Codex Works
How CockroachDB Built Vector Indexing at Scale
ByteByteGo · 2026-05-25 · via ByteByteGo Newsletter

Most AI agents don’t fail because of the model. They fail because the context is broken—stale data, fragmented systems, slow retrieval.

Join Simba Khadder, Head of AI Product & Director of Software Engineering at Redis, on June 10 to see how to turn scattered enterprise data into live, agent-ready context with Redis Iris.

You’ll learn:

  • The four failure modes of how context breaks in production

  • How to make your enterprise data navigable for runtime

  • How Redis Context Retriever, Search, Data Integration, and Agent Memory work together

Reserve your spot →

The CockroachDB team wanted to add vector search to their distributed database, and dozens of well-known algorithms already existed.

To facilitate the decision-making process, they wrote down a list of architectural requirements, including a refusal to depend on any central coordinator, a refusal to allocate large in-memory caches, a need for real-time updates, an intolerance for hot spots, and a requirement to support sharding. Then they checked the list against the popular options.

Most failed at least one requirement, and some failed several. The team’s response was to build something new, called C-SPANN, that satisfied every constraint by treating the index as ordinary table data inside CockroachDB rather than as a separate system.

In this article, we will look at how the CockroachDB engineering team built this index and the challenges they faced.

Disclaimer: This post is based on publicly shared details from the CockroachDB Engineering Team. Please comment if you notice any inaccuracies.

A vector is a long list of numbers that captures the meaning of something.

Modern neural networks like the ones behind ChatGPT can take an image, a document, or a snippet of audio and convert it into a vector of floating-point numbers, typically a few hundred to a few thousand dimensions long.

The useful property of these vectors, often called embeddings, is that similar things produce similar vectors. For example, two photos of beaches end up close to each other in this multi-dimensional space, and a photo of a beach and the word “beach” end up in roughly the same neighborhood, which is what makes semantic search possible.

The trick is finding those neighbors quickly when you have billions of vectors to search through.

Traditional database indexes work because numbers and strings have a natural ordering. We can sort them, store them in a B-tree, and walk that tree to find what you want.

Vectors do not have that property. Should beach photos come before or after food photos? What about photos of food at the beach? There is no answer, because the data has no inherent sequence, which means a B-tree cannot help you.

The brute-force alternative is to compare your query vector against every stored vector and return the closest matches. This works fine for a few thousand vectors, but falls apart somewhere in the tens of thousands, and becomes hopeless once you reach the millions.

Vector indexes solve this by giving up on exact answers. They find approximate nearest neighbors, accepting a small loss of accuracy in exchange for orders of magnitude better performance. The results are usually close enough that real users cannot tell the difference, and the search runs fast enough to feel instant. That tradeoff between accuracy and speed is the foundation of every vector index, and the interesting engineering question is how you make the rest of the system work around it.

Even with a good algorithm for finding nearest neighbors, plugging it into a distributed transactional database is its own problem. That is where the CockroachDB story actually begins.

CockroachDB is a distributed SQL database. This means that the data lives across multiple machines, often across regions, and the system is designed to scale linearly. It guarantees transactional consistency and supports real-time updates, and all of this has to keep working when machines die, disks fail, or networks partition.

These properties impose a set of architectural constraints on any new feature, and a vector index is no exception. The CockroachDB team wrote down six requirements that any candidate algorithm had to satisfy.

  • The first requirement is that no single node can act as a central coordinator. Any node in the cluster should be able to serve reads and writes, because relying on a single leader to direct traffic creates a bottleneck and a single point of failure.

  • The second requirement is that the index cannot rely on large in-memory structures. Index state has to live in persistent storage, since the team could not assume every node has gigabytes of RAM available for caching vectors. They also wanted to avoid the long warm-up times that come with rebuilding in-memory caches after a restart, which matters especially for serverless deployments where nodes spin up and down on demand.

  • The third requirement is that network hops have to stay minimal. Round-trips between nodes are expensive, and any algorithm that requires sequential traversal across the cluster will accumulate latency unpredictably.

  • The fourth requirement is that the index data layout has to be sharding-compatible. Index data has to map naturally to CockroachDB’s key-value storage so that it can be split, merged, and rebalanced like any other table.

  • The fifth requirement is that the index must avoid creating hot spots. As inserts and queries scale up, the load has to spread across the cluster, because concentrating traffic on a single node defeats the point of running a distributed system in the first place.

  • The sixth requirement is that the index has to support incremental updates. Inserts and deletes need to be applied in real time without blocking queries, requiring batch rebuilds, or degrading search quality over time.

This list rules out the most popular vector indexes.

HNSW, the graph-based algorithm that powers pgvector, Weaviate, and many other systems, is excellent on accuracy benchmarks but builds its graph in memory and resists sharding. Classic IVF is closer in spirit but assumes a single-node deployment and struggles with dynamic updates. Specialized vector databases like Pinecone solve these problems by being separate systems entirely, which works fine if you are willing to keep your vectors in one database and your transactional data in another.

CockroachDB needed something that handled both inside the same system, with the same guarantees.

Faced with this list, the team built something new. They called it C-SPANN, and the design choices that make it work are mostly about what it does not try to do.

C-SPANN borrows ideas from three places.

Microsoft’s SPANN paper contributed the tree structure for partitioning vectors, the follow-up SPFresh paper contributed techniques for incremental updates, and Google’s ScaNN project contributed ideas around quantization.

The CockroachDB team combined these with their distributed SQL architecture to produce something none of the source papers describe directly.

At the core is a hierarchical K-means tree. Vectors are grouped into partitions based on similarity, where each partition typically contains dozens to hundreds of vectors and has a centroid that represents the average of the vectors it contains. Think of the centroid as the partition’s center of mass. Those centroids are themselves grouped into higher-level partitions with their own centroids, and that process repeats until you reach a single root partition at the top.

The result is a wide, shallow tree. With a fanout of around 100, an index of one million vectors needs only three levels, and an index of ten billion vectors needs only five. Searching the tree means starting at the root, comparing the query vector to the centroids at that level, descending into the closest partition, and repeating until you reach the leaves. At each level, partitions can be processed in parallel, which keeps latency low and predictable. At the leaves, the system scans a few hundred candidate vectors using SIMD CPU instructions for speed.

That much describes the algorithm. The interesting part is what happens to the data structure once it is built.

Each partition is stored as a self-contained set of key-value rows inside CockroachDB. Partition data lives in CockroachDB ranges, which are the same units of storage that hold every other table in the database. Therefore, the index is not a parallel structure attached to the database. It is table data with extra meaning.

This decision pays dividends. CockroachDB already knows how to split a range when it grows too large, how to rebalance ranges across nodes when load shifts, and how to cache frequently accessed rows in its block cache.

All of this setup applies to vector index data automatically, without writing a single line of new infrastructure code. When a new node joins the cluster, ranges containing index partitions get distributed to it the same way ranges containing user tables do. When a node restarts, the index is immediately ready to serve queries because it lives on disk, rather than in some warm-up cache that has to be rebuilt.

However, building the index is one thing. Keeping it healthy as data flows in and out is harder, especially when the index needs to be compressed aggressively to stay affordable.

A K-means tree is not static. Partitions grow as vectors are inserted and shrink as vectors are deleted, so the system needs background machinery to keep partitions at a reasonable size and to keep vectors grouped with their nearest centroids.

When a partition grows too large, C-SPANN splits it. A balanced variant of the K-means algorithm divides the vectors into two roughly equal groups, each with its own new centroid, and the tree is updated so that future inserts route to whichever new partition is closer. When a partition shrinks too small, the system merges it away and reassigns its vectors to neighboring partitions. Both operations happen in the background to avoid interfering with foreground transactions.

There is one point worth noting.

After a split, some vectors in the original partition might actually be closer to a neighboring partition’s centroid than to either of the two new centroids, and they get reassigned. Likewise, a vector in a neighboring partition might now be closer to one of the new centroids and migrate in. This idea, called nearest partition assignment, comes from the SPFresh paper and is what keeps the index accurate over time.

The consequence is that you can start with an empty table, insert millions of vectors, and end up with an accurate, well-balanced index without ever rebuilding it. The maintenance setup handles everything incrementally.

The other operational factor is size. An OpenAI embedding has 1,536 dimensions stored as 2-byte floats, which works out to about 3 KB per vector. A billion vectors at full precision is 3 TB just for the embeddings, before any indexing overhead is counted. Storing and scanning that much data is expensive both in disk space and in the CPU and memory used during search.

C-SPANN compresses vectors using a technique called RaBitQ, which reduces each dimension to a single bit. The compressed representation is roughly 200 bytes per vector, a 94 percent size reduction. The math behind the compression involves a random orthogonal transform that preserves distances while spreading data evenly across dimensions

What matters for the system is that quantization is lossy, so distance estimates from compressed vectors are only approximate. C-SPANN compensates with a reranking step, where the system scans quantized vectors to assemble a candidate set, then fetches the original full-precision vectors for those candidates to compute exact distances. By fetching candidates, the system can absorb quantization error and still return accurate results. The pattern of cheap approximate filtering followed by precise refinement on a small candidate set shows up in many other systems too, and recognizing it here makes it easier to spot elsewhere.

The third operational reality is multi-tenancy. In most real applications, vectors belong to someone, whether a user, a customer, or an organization, and most queries are scoped to a single owner. Mixing one user’s vectors with another’s during search is wasteful, and it is also a security problem.

CockroachDB handles this through prefix columns on the vector index. Here is what the schema looks like.

A query for one user’s nearest matches uses pgvector-compatible syntax.

Behind the scenes, the index maintains a separate K-means tree for each distinct user. Performance scales with how many vectors the user owns, rather than with the total size of the index, so a billion vectors split across a million users behaves, from any one user’s perspective, like a million-vector index.

Combined with CockroachDB’s REGIONAL BY ROW tables, prefix columns can also partition the index by geography. For example, a user in Europe gets their data and their index entries stored in a European region, with fast local access and compliance with data domiciling requirements, while the same table serves a US user with equally low latency from US infrastructure. The combination of region, ownership, and embedding as prefix columns produces an index that is efficient, secure, and locality-aware by default.

C-SPANN refused several compromises that most vector databases quietly accept.

Freshness in CockroachDB is real-time and transactional rather than batched or eventually consistent, which means a vector inserted in a transaction becomes searchable as soon as that transaction commits, with the same consistency guarantees as any other write. Scaling is native to the distributed architecture rather than a feature retrofitted onto a single-node system, and vectors live alongside transactional data in the same database, inside the same queries, under the same operational umbrella. Since the index lives on disk, nodes serve queries immediately after a restart without any warm-up phase.

In return, the team accepted some real limitations. The 25.2 release is a preview, and several optimizations are still being built, including fuller SIMD usage, root partition caching, and complete merge support. The current implementation supports only Euclidean distance, with cosine and inner product on the roadmap. Filtering on non-prefix columns is limited today, though that scope is expanding. Also, on raw vector search benchmarks against specialized in-memory systems, C-SPANN does not win on pure latency.

The tradeoff suggests where this design fits and where it does not. CockroachDB’s vector index is a strong choice for applications where vectors and transactional data need to coexist, where multi-tenant isolation matters, and where multi-region deployment with data domiciling is a requirement. Specialized vector databases remain a better fit for pure vector workloads with no transactional component, for read-heavy batch-updated datasets where freshness is not a concern, and for cases where every microsecond of search latency is critical.

The architectural logic underneath all of this is worth keeping in mind. The CockroachDB team treated the vector index as ordinary table data and inherited their existing distributed machinery for free, so splits, caching, sharding, replication, and multi-region behavior all worked from day one because they already worked for everything else in the database. The algorithm is the part that gets the headlines, but the integration is what makes the system possible.

References:

Discussion about this post

Ready for more?