LanceDB
A serverless vector database built on the Lance columnar format: fast scans over multimodal data with embedded ANN indexes.
LanceDB is a serverless vector database whose file format (Lance) is a columnar container optimized for fast random access to large blobs (images, audio, video) plus vector search. It supports embedded use (Node, Python) and a server mode, and ships IVF-PQ and HNSW indexes that live inside the same file as the data.
Lance columnar format
A row group per fragment, column-major storage inside each group, plus a row index for fast point lookups. Fragments can be added cheaply and the file is append-friendly.
Vector indexes inside the file
IVF-PQ and HNSW indexes are stored as part of the dataset. Queries run against the index without unpacking the full column.
Serverless or server
The same Rust core runs in-process (no socket, no daemon) or behind a REST API. Pick based on scale.
Multimodal friendly
Native support for image, audio, and video types via the Arrow ecosystem. Embeddings are just one more column.
The Lance file
A Lance file is a directory of fragments plus metadata. Each fragment has N rows; inside a fragment, columns are stored as Arrow IPC chunks. A separate row index maps primary keys to fragment + offset. The result is a file that supports fast column scans (for vector search) and fast row lookups (for primary-key reads).
Fragments can be added in O(1) and the file stays a single object for the consumer. This makes Lance popular for lakehouse-style data over S3 or local disk.
ANN search
LanceDB ships IVF-PQ (good for very large datasets) and HNSW (better recall). Both are built into the Rust core; queries are dispatched there directly. The query plan is simple: load the index, traverse it, fetch the top-k rows from the column store by their row-id, return.
import lancedb
db = lancedb.connect("./.lancedb")
tbl = db.create_table("docs", data=rows, mode="overwrite")
tbl.create_index(num_partitions=256, num_sub_vectors=96) # IVF-PQ
results = (
tbl.search([0.1, 0.2, 0.3, ...])
.metric("cosine")
.limit(10)
.to_pandas()
)
Why serverless wins for many workloads
For RAG over a few million embeddings, the cost of running a separate Qdrant or Milvus cluster is hard to justify. LanceDB runs inside the same Python process as the rest of your pipeline: no socket, no auth, no separate ops surface. You pay for the Rust binary and the file. When you outgrow that, you turn on server mode without changing the data layout.
Diagrams
probed 1/4 clusters · scanned 6/24 vectors · skipped 18 · recall 0.50 vs exact · top-4: 10, 17, 13, 2
IVF's coarse quantizer prunes clusters by centroid distance; the tradeoff is visible — vectors in skipped clusters can never appear in the result, so recall is capped by the probe count. Product quantization (the PQ in IVF-PQ) is a separate compression step applied inside each cluster's scan; it is not the partitioning shown here.
Vectors are partitioned into Voronoi cells (IVF), and each vector is compressed via product quantization (PQ) into a few bytes. Queries probe nearby cells and approximate distances via lookup tables.
A query vector is quantized, routed to the nearest IVF cell, and compared against PQ codes in nearby cells; the top-k candidates are re-ranked by exact distance.
Lance file format uses append-only versioning; a write creates a new chunk, and reads pick the latest version per row.
A query enters the top layer, hops across neighbors, and drills down to the densest base layer for the top-k.