Lance

Lance is an open-source columnar data format optimized for machine-learning and AI workloads. Built by LanceDB, Lance is positioned as a successor to Parquet for ML use cases — cheap random access, native vector embeddings, automatic versioning, and zero-copy reads into PyTorch / NumPy / Arrow without a deserialization step. LanceDB, the embedded vector database, is built directly on the Lance format.

1. How It Is Organised

Lance Dataset Layout Manifest (version N)schema, fragment list, indexesFragment 1data files per column groupFragment 2data files per column groupFragment 3appended laterIndexesIVF_PQ vectors, scalarDeletion filessoft deletes per fragmentEach write creates a new manifest; older manifests remain, which is what makes time travel and rollback possible.

A Lance dataset is a directory of immutable fragments plus a series of versioned manifests. Each fragment holds a range of rows, stored column by column in Lance's own file format, which uses encodings designed so that any single row can be located without decoding a large page. The manifest for each version lists the fragments, the schema and any indexes. Appends add fragments; deletes write small deletion files rather than rewriting data; adding a column writes new column files for existing fragments. Readers open a specific manifest, so they always see a consistent version.

2. Key Features:

3. Lance vs. Parquet:

4. Use Cases:

Vector Search in a Lance Dataset Query vectorembeddingIVFpick nearest partitionsPQ scanapproximate distancesRefineoptional exact rerankTop-k rowswith filters, columnsnprobes controls recall vs latency; refine_factor re-ranks candidates with full vectors.

Lance can build approximate nearest neighbour indexes on vector columns. The common IVF_PQ index first clusters vectors into partitions (IVF), then compresses each vector into a short product-quantisation code (PQ). At query time Lance probes the partitions nearest to the query, computes approximate distances on the compressed codes, and optionally re-ranks the best candidates with the original vectors. Because the index lives inside the dataset, the same files serve as the training dataset, the feature store and the search index.

6. Python Example

import lance, numpy as np, pyarrow as pa   # pip install pylance

n, dim = 10_000, 128
vectors = np.random.rand(n, dim).astype("float32")
table = pa.table({
    "id": pa.array(range(n)),
    "label": pa.array(["cat" if i % 2 else "dog" for i in range(n)]),
    "vector": pa.FixedSizeListArray.from_arrays(pa.array(vectors.ravel()), dim),
})
lance.write_dataset(table, "animals.lance")              # version 1

ds = lance.dataset("animals.lance")
print(ds.take([5, 500, 5000], columns=["id", "label"]))   # random access by row

ds.create_index("vector", index_type="IVF_PQ",
                num_partitions=64, num_sub_vectors=16)
hits = ds.to_table(nearest={"column": "vector", "q": vectors[0], "k": 5},
                   filter="label = 'dog'", columns=["id", "label"])
print(hits)

old = lance.dataset("animals.lance", version=1)           # time travel

The take call is the operation Lance is optimised for: fetching scattered rows, as a shuffled data loader or a retrieval step does, without reading whole row groups. LanceDB adds a database API (tables, full-text search, hybrid search) on top of the same format.

7. When to Use It

WorkloadLanceParquet / Iceberg / Delta
Full scans and aggregations for BIWorks, not its focusBest fit, broad engine support
Random row access, shuffled training readsBest fitSlow: must decode row groups
Embeddings with ANN searchBuilt inNeeds a separate vector database
Images, audio and large blobs next to metadataDesigned for itPossible but awkward
Multi-engine SQL (Trino, Snowflake, Athena)LimitedStrong

8. Pitfalls