10 min read

The Billion-Vector Wall: Architecting Multi-Tenant Vector Search with Segmented LSM-Trees

Multi-Tenant Billion-Vector Search via Segmented LSM-Trees

The Billion-Vector Wall: Architecting Multi-Tenant Vector Search with Segmented LSM-Trees

So, you’ve built a Retrieval-Augmented Generation (RAG) prototype. It works beautifully on your laptop with 10,000 document chunks. You’re feeling like a wizard. But then, the enterprise requirements hit the fan: “We need this to support 5,000 distinct B2B customers, each with their own isolated data, sub-millisecond latency, and we’re looking at a total corpus of about two billion vectors.”

Suddenly, that “simple” vector database you picked up doesn’t look so sturdy.

The reality of modern AI infrastructure is that vector search is easy, but multi-tenant vector infrastructure is a nightmare. When you move from a single-user sandbox to a global scale, the laws of physics start to fight you. You aren’t just looking for a needle in a haystack anymore; you’re looking for five thousand different needles in five thousand different haystacks, all while sharing the same harvester.

Today, we’re going deep into the engine room. We’re going to talk about how to architect a multi-tenant vector database that doesn’t melt under the pressure of a billion vectors, focusing on the intersection of Segmented Indexing and LSM-Tree (Log-Structured Merge-Tree) optimization.


The Hype vs. The Hard Truth

If you’ve been following the AI hype cycle, you know that vector databases (Pinecone, Weaviate, Milvus, Qdrant, etc.) became the “gold rush” picks and shovels of 2023. The narrative was simple: “LLMs have no memory; vectors are the memory.”

But as the dust settles, the engineering community is realizing that the “flat” approach to vector indexing—where you just throw everything into a single HNSW (Hierarchical Navigable Small World) graph—doesn’t work for SaaS. In a SaaS environment, Tenant A’s data must never be visible to Tenant B. More importantly, Tenant A’s massive bulk upload shouldn’t spike the latency for Tenant B’s real-time query.

This is the Noisy Neighbor problem amplified by the extreme compute intensity of high-dimensional distance calculations. To solve this, we have to borrow a page from the book of classic database internals—specifically, the LSM-tree—and marry it with modern approximate nearest neighbor (ANN) algorithms.


The Architecture of Isolation: Logical vs. Physical

In a multi-tenant system, you have three ways to split the data:

  1. Shared Index, Metadata Filtering: All tenants are in one big HNSW graph. You filter by tenant_id at query time. Warning: This is a performance trap. If Tenant A has 100 million vectors and Tenant B has 100, the search still traverses the massive graph, leading to horrific latency for the small tenant.
  2. Index-per-Tenant: Every tenant gets their own graph. Warning: This is a memory trap. HNSW graphs are memory-hungry. If you have 10,000 tenants, the overhead of the graph structures alone will bankrupt your RAM budget.
  3. Segmented Multi-Tenancy: This is the “Goldilocks” zone. You group tenants into segments, using an LSM-tree approach to manage how these segments are written, merged, and searched.

Traditional databases like RocksDB or Cassandra use LSM-trees because they turn random writes into sequential writes. For vector databases, this is a game-changer.

In a classic vector index, if you want to add a single vector to an HNSW graph, you have to find its neighbors, update pointers, and potentially rebalance layers. Doing this at high frequency is a recipe for lock contention and CPU thrashing.

The Write Path: Memtables and WAL

When a new vector arrives for a specific tenant, we don’t immediately bake it into a giant global index. Instead:

  1. We append the vector and its metadata to a Write-Ahead Log (WAL) for durability.
  2. We insert it into an in-memory Vector Memtable.
  3. This Memtable is often a small, unoptimized index (like a flat buffer or a small IVF index) that is extremely fast to write to but “good enough” to search.

The Flush: Transforming to Segments

Once the Memtable hits a size threshold (e.g., 64MB or 100,000 vectors), we “flush” it to disk as a Segment.

A Segment is an immutable, read-only file containing:

  • A localized HNSW graph.
  • Compressed vector data (usually via Product Quantization).
  • A Bloom filter for metadata tags.

This is where the magic happens. By creating immutable segments, we can scale horizontally. Tenant A’s data might be spread across 10 segments. To search Tenant A’s data, we only search those 10 segments.


Segmented Indexing: Breaking the HNSW Bottleneck

HNSW is the industry standard for vector search because it’s incredibly fast. But it has a fatal flaw: it’s not naturally “mergeable.” You can’t just take two HNSW graphs and zip them together like a B-Tree.

The Compaction Strategy

In a billion-scale system, you end up with thousands of small segments. Searching across 1,000 segments is slow (the “fan-out” problem). We need to merge them. This is Compaction.

During compaction, our background workers:

  1. Pick several small segments belonging to the same tenant (or group of tenants).
  2. Read the raw vectors.
  3. Re-build a new, larger, and more optimized HNSW graph.
  4. Atomically swap the old segments for the new one.

Engineering Curiosity: The “Tombstone” Problem How do you delete a vector in an immutable segment? You can’t reach into the file and erase it. Instead, we use Tombstones. We keep a bitset of deleted IDs. During a search, if a vector is a “hit” but its bit is set in the tombstone, we discard it. During compaction, we finally purge those vectors for good.


Solving the “Noisy Neighbor” with Compute/Storage Separation

At the billion-vector scale, you cannot keep all vectors in RAM. If each vector is 1,536 dimensions (standard for OpenAI text-embedding-3-small) using float32, a single vector is 6KB.

  • 1 million vectors = 6GB.
  • 1 billion vectors = 6 Terabytes.

No cloud instance is going to give you 6TB of RAM without costing more than your company’s ARR.

Product Quantization (PQ) and Scalar Quantization (SQ)

To make this work, we use Quantization. We compress those 32-bit floats into 8-bit integers (SQ) or, even better, use Product Quantization.

PQ works by splitting a vector into chunks (sub-spaces) and clustering those chunks. Instead of storing the vector, we store “cluster IDs” (centroids). This can compress a vector by 10x to 50x with minimal loss in recall.

The Tiered Storage Model

Our architecture separates the Query Nodes from the Data Nodes:

  • Hot Tier (Local NVMe): The most frequently accessed segments and HNSW graph structures (the “navigation” part of the graph) stay on local NVMe SSDs. We use mmap to map these files into memory, letting the OS kernel handle page caching.
  • Warm/Cold Tier (S3/GCS): The raw, full-resolution vectors stay in object storage. We only fetch them if we need to perform a “re-ranking” step to improve accuracy after the initial ANN search.

Scaling the Compute: SIMD and Parallelism

When you’re searching a billion vectors, every CPU cycle counts. Modern CPUs (Intel Ice Lake+, AMD Milan+) have AVX-512 or ARM Neon instructions. These allow for SIMD (Single Instruction, Multiple Data) operations.

In a standard dot-product calculation:

// The "Slow" Way
float dot_product(float* a, float* b, int n) {
    float sum = 0;
    for (int i = 0; i < n; i++) {
        sum += a[i] * b[i];
    }
    return sum;
}

This is a bottleneck. Our engine uses SIMD-optimized kernels that can calculate distances for 16 or 32 dimensions in a single clock cycle. When you multiply this across 64 cores, you can achieve millions of distance calculations per millisecond.


The “Global Index” Myth

One of the most interesting engineering curiosities in this space is the realization that a Global Index is often a liability.

If you are building a multi-tenant system for 10,000 customers, and you put them all in one index, you face the “Tail Latency of Death.” One customer’s complex query or massive update can lock the global index structure, causing a latency spike for everyone.

By moving to a Segmented LSM-Tree architecture, we achieve Performance Isolation. We can route Tenant A’s queries to a specific set of CPU cores or even a specific set of nodes, ensuring that Tenant B is completely unaffected.

Routing Logic: The “Tenant-Aware” Load Balancer

Our gateway doesn’t just round-robin requests. It maintains a Segment Map. When a request comes in for tenant_789, the gateway knows exactly which nodes hold the segments for that tenant.

# Simplified Routing Logic
def handle_query(tenant_id, query_vector):
    # 1. Look up where this tenant's segments live
    nodes = segment_registry.get_nodes_for_tenant(tenant_id)

    # 2. Scatter the query to those nodes
    results = parallel_execute(nodes, lambda node: node.search(tenant_id, query_vector))

    # 3. Gather and merge-sort the top-K results
    final_top_k = merge_and_rerank(results)
    return final_top_k

Infrastructure at Scale: The Billion-Vector Math

Let’s look at the actual infrastructure footprint for a 1-Billion Vector Cluster using the architecture we’ve discussed.

Assumptions:

  • Vector Dim: 768 (Standard for many open-source models).
  • Compression: 4x (Scalar Quantization to int8).
  • Indexing: HNSW ($M=16$, $efConstruction=200$).

The Storage Math:

  • Raw Vectors (Compressed): $1,000,000,000 \times 768 \text{ bytes} \approx 768 \text{ GB}$.
  • HNSW Overhead (Pointers/Graphs): $\approx 200 \text{ GB}$.
  • Total Data Footprint: $\approx 1 \text{ TB}$.

The Compute Math: To handle 500 Queries Per Second (QPS) with <50ms latency, we need to distribute that 1TB across a cluster. Using nodes with 128GB of RAM, we would need roughly 8-10 nodes. This allows us to keep the “hot” part of the graph in memory while the vectors themselves are mmap’d from NVMe.


Challenges and Engineering Trade-offs

No architecture is perfect. The Segmented LSM-tree approach has its own demons.

1. The Merge Storm

When many tenants are writing data simultaneously, the background compaction processes can start competing with foreground queries for CPU and I/O. This is the “Merge Storm.”

  • Solution: We implement IO Throttling and Tiered Compaction. We prioritize merging smaller segments to keep the segment count low, but we delay the massive “Level 2 to Level 3” merges for off-peak hours.

2. Consistency vs. Availability

In a distributed system, do you want “Read-Your-Writes” consistency? If a tenant adds a vector, do they need to see it in a search 10 milliseconds later?

  • The Trade-off: True consistency requires flushing the Memtable and updating the graph immediately, which kills throughput. Most billion-scale systems settle for Eventual Consistency (usually a 1-5 second lag) to allow for efficient batching.

3. Memory Fragmentation

With thousands of tenants and millions of segments, memory fragmentation in the heap can become a silent killer.

  • The Fix: We use Off-heap Memory Management. We allocate large chunks of memory using DirectByteBuffers (in Java/Kotlin) or jemalloc (in C++/Rust) and manage the layout manually to avoid the overhead of the Garbage Collector.

Why This Matters for the Future of AI

We are moving away from “AI as a feature” to “AI as the operating system.” In that world, your vector database isn’t just a cache; it’s the primary data store for the model’s “long-term memory.”

The transition from single-tenant HNSW to Multi-Tenant Segmented LSM-Trees is the same transition the industry made from simple file-based storage to relational databases like PostgreSQL. It’s about maturity, reliability, and the ability to scale without the costs scaling linearly.

Building for a billion vectors isn’t just about “bigger servers.” It’s about a fundamental shift in how we think about data locality, compression, and the lifecycle of a search index. By treating vector indices as immutable segments within an LSM-tree structure, we unlock the ability to serve the world’s largest enterprises with the same ease that we served our first prototype.

The billion-vector wall is real. But with the right architecture, it’s not a barrier—it’s a foundation.


More to explore

Keep diving in