lance-format / lance-format/lance

Cluster base data in accordance with index structure

Open
#9,266 1 comment 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

feature
Dominant language
Rust
Stars
7.1k
Forks
852
Avg merge
3d 18h
Merged PRs (30d)
272

Description

Dear colleagues, this is related to Issue #2064, but I decided to create a separate Issue, since it is more general (not confined to HNSW). Without data clustering, processing top-k queries for huge k currently is infeasible over object storage (henceforth OBS).

Current situation

Scenario: Top-k query for large k over an IVF index in Lance, data is stored in OBS. In this case, the bottleneck is not the index search, but the "take" phase, when the result vectors are requested over OBS.

The result vectors are needed for:

  • for getting all required result columns from the result vectors or
  • for computing exact distances during reranking.

Currently, the result vectors are scattered within fragments and across many fragments. Thus, Lance cannot group the requests into a few range I/O requests, leading to hundreds of thousands of random access requests to OBS. It also makes local caches largely useless.

Solution proposal

Main idea

So as to exploit the fact that all requested row IDs come from a subset of IVF partitions, reorder base table rows so that rows from
the same IVF partitions will be close on disk.

Challenges that need to be addressed

  • Challenge 1: Reorder the base table without duplicating the index building work.
  • Challenge 2: There may be more than one index over the data set, so reordering the base data according to one vector index will not help accelerating the "take" phase during search over other indices.
  • Challenge 3: Ingestion of new data needs not only updating the vector index, but also needs to maintain the order of the base data according to the index / indices.

Approach

Towards Challenge 1: Interleaved index building and base data reordering

In order not to require index rebuilding after base table reordering, use the following process.

  • Step 1: Lance workers train IVF centroids on a data sample (in the same way as it is done now).
  • Step 2: Instead of the current procedure of assigning all vectors to IVF partitions, a job is started (potentially a
    distributed job involving Spark) that
    • for each vector in the base data set finds the correct IVF partition,
    • writes the vector into a Lance fragment corresponding to IVF partition,
    • merges and repartitions the fragments so that vectors from the same IVF partition lie in the same fragment and are sorted by partition ID.
  • Step 3: Lance then writes the partition postings according to the reorganized data fragments.
Towards Challenge 2: Space-filling curves

The above procedure optimizes the base data layout according to one IVF index. As a result

  • queries over this IVF index will profit from the accelerated "take" phase,
  • queries over other indices (vector or scalar) will still suffer from hundreds of thousands of random point requests.

As an example, let's say we have different IVF indices built over the same data set. Then the goal is to reorder and repartition the data set so that

  • if two vectors are in the same partition within any IVF index,
  • then the two vectors will be close in base data layout on disk.

For a small number of indices (say three), one can order the data according to a space filling curve.

  • A space filling curve can map 3D coordinates into 1D space (a linear order) so that points close in the 3D space will be close in the linear order.
  • In this particular example, the coordinates in 3D space are just the three partition IDs of the record in the three IVF indices.

Ordering the data according to such a space filling curve will improve data locality and thus

  • allow grouping point requests into physical range I/O requests more frequently,
  • improve cache efficiency by improving data locality (pages will already be in cache).

Examples for such space filling curves

  • Z-order (for 2D)
  • Hilbert Curves.

They are used, for example, by DataBricks in dynamic data clustering. Also, I see that Lance already contains a Hilbert curve implementation for its scalar R-Tree spatial index.

Towards Challenge 3: LSM-style ingestion and compaction

To maintain index validity and the physical data layout according to partition IDs (or the space filling curves), ingest new data LSM-style, where SST files are kept sorted and are compacted according to the order given by the space filling curve.

This will at least reduce the work of ingesting and index building up to the point where, due to distribution drift of the ingested data, centroid quality and recall degenerate. In this case, the centroids, index partitions, and data layout need to be recomputed.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the existing IVF index-building flow, the take phase over object storage, fragment repartitioning, and the existing Hilbert-curve implementation for the scalar R-Tree. Compare the proposed layouts for one or multiple IVF indices and the LSM-style ingestion approach. Done requires a validated design that addresses reordering, index validity, multiple indices, and ongoing ingestion without redundant index rebuilding.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
data-engineering, performance, search
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.