探秘新一代向量存储格式Lance-format (一)Lance 项目概览与设计理念

第一章:Lance 项目概览与设计理念

核心概览

Lance 是一个面向多模态 AI 工作流的列式数据格式。它解决的根本问题是:如何在统一的存储格式中,高效地支持向量搜索、随机访问、SQL 查询和多模态数据存储

想象您在构建一个视频推荐系统:

  • 需要存储视频元数据(标题、描述、标签)
  • 需要存储视频的向量嵌入用于相似度搜索
  • 需要存储原始视频数据(BLOB)
  • 需要在这些数据上执行复杂的 SQL 查询和向量搜索

Lance 就是为这样的场景设计的。

Lance 主架构设计

graph TB subgraph "应用层 Application Layer" A1["Python API
(PyLance)"] A2["Java API
(JNI)"] A3["Rust API"] end subgraph "查询引擎层 Query Engine" B1["DataFusion 集成
lance-datafusion"] B2["SQL 查询
解析与规划"] B3["Scanner
查询执行"] end subgraph "数据集层 Dataset Layer" C1["Dataset
版本管理"] C2["Transaction
事务处理"] C3["Fragment
数据分片"] C4["Schema
演化管理"] end subgraph "索引层 Index Layer" D1["向量索引
IVF/HNSW/FLAT"] D2["标量索引
BTree/Bitmap"] D3["全文索引
Tantivy"] end subgraph "表管理层 Table Layer" E1["Manifest
lance-table"] E2["Commit 协议
版本控制"] E3["RowID 系统
行地址管理"] end subgraph "存储层 Storage Layer" F1["文件格式
lance-file"] F2["编码器
lance-encoding"] F3["IO 调度
lance-io"] end subgraph "核心层 Core Layer" G1["类型系统
lance-core"] G2["缓存管理"] G3["Arrow 集成
lance-arrow"] end subgraph "对象存储 Object Storage" H1["S3"] H2["Azure Blob"] H3["GCS"] H4["本地文件系统"] end A1 --> B1 A2 --> B1 A3 --> C1 B1 --> B3 B2 --> B1 B3 --> C1 C1 --> C2 C1 --> C3 C1 --> C4 C1 --> D1 C1 --> D2 C3 --> E1 C2 --> E2 C1 --> E3 D1 --> F1 D2 --> F1 D3 --> F1 E1 --> F1 E2 --> F1 E3 --> F1 F1 --> F2 F1 --> F3 F2 --> G1 F3 --> G1 G1 --> G3 F3 --> H1 F3 --> H2 F3 --> H3 F3 --> H4

核心功能时序图

1. 数据写入流程

sequenceDiagram participant User participant Dataset participant Transaction participant Writer participant Encoder participant ObjectStore participant Manifest User->>Dataset: write_dataset(data, uri) Dataset->>Transaction: begin_transaction() Transaction->>Writer: create_writer(schema) loop 每个 RecordBatch Writer->>Encoder: encode_batch(batch) Encoder->>Encoder: 选择编码方式 Encoder->>ObjectStore: write_data_file() ObjectStore-->>Encoder: 确认写入 end Writer->>Dataset: 返回 Fragments Dataset->>Manifest: create_new_version() Manifest->>ObjectStore: write_manifest() Dataset->>Transaction: commit() Transaction->>ObjectStore: 原子性提交 Transaction-->>User: 返回新版本

2. 数据扫描流程

sequenceDiagram participant User participant Scanner participant Dataset participant Fragment participant FileReader participant Decoder participant Cache User->>Scanner: scan().project([columns]) Scanner->>Scanner: build_execution_plan() Scanner->>Dataset: get_fragments() Dataset-->>Scanner: Fragment列表 loop 每个 Fragment Scanner->>Fragment: open_reader() Fragment->>FileReader: create_reader() FileReader->>Cache: check_cache() alt 缓存命中 Cache-->>FileReader: 返回缓存数据 else 缓存未命中 FileReader->>Decoder: decode_page() Decoder->>Decoder: 解压与解码 Decoder-->>FileReader: RecordBatch FileReader->>Cache: store_cache() end FileReader-->>Scanner: RecordBatch end Scanner-->>User: Stream

3. 向量索引创建流程

sequenceDiagram participant User participant Dataset participant IndexBuilder participant KMeans participant Quantizer participant IVFStorage participant ObjectStore User->>Dataset: create_index(column, IVF_PQ) Dataset->>IndexBuilder: build_index() IndexBuilder->>Dataset: scan_vectors() Dataset-->>IndexBuilder: 向量数据流 IndexBuilder->>KMeans: train_centroids(vectors, k) KMeans->>KMeans: 迭代聚类 KMeans-->>IndexBuilder: 质心列表 IndexBuilder->>Quantizer: train_pq(vectors) Quantizer->>Quantizer: 学习 codebooks Quantizer-->>IndexBuilder: PQ 模型 IndexBuilder->>IndexBuilder: assign_to_partitions() loop 每个分区 IndexBuilder->>Quantizer: quantize_vectors() Quantizer-->>IndexBuilder: PQ codes IndexBuilder->>IVFStorage: write_partition() IVFStorage->>ObjectStore: 持久化 end IndexBuilder->>Dataset: update_index_metadata() Dataset->>ObjectStore: 更新 Manifest Dataset-->>User: 索引创建完成

4. 向量搜索流程

sequenceDiagram participant User participant Scanner participant VectorIndex participant IVF participant Quantizer participant PostFilter participant Ranker User->>Scanner: nearest(query, k=10) Scanner->>VectorIndex: search(query_vector) VectorIndex->>IVF: compute_distances_to_centroids() IVF-->>VectorIndex: 排序的分区列表 VectorIndex->>VectorIndex: select_nprobes(nprobes) loop 对于每个选中的分区 VectorIndex->>IVF: load_partition() IVF->>Quantizer: approximate_distances() Quantizer-->>IVF: 近似距离列表 IVF->>Ranker: add_candidates() end Ranker->>Ranker: top_k_selection() Ranker->>PostFilter: refine_distances() PostFilter->>PostFilter: 计算精确距离 PostFilter-->>Scanner: 最终 Top-K 结果 Scanner-->>User: RecordBatch with scores

5. 事务提交流程

sequenceDiagram participant Client participant Transaction participant CommitHandler participant DynamoDB participant ObjectStore participant Manifest Client->>Transaction: commit_changes() Transaction->>Transaction: validate_operations() Transaction->>Manifest: build_new_manifest() Manifest-->>Transaction: manifest_data Transaction->>CommitHandler: begin_commit() alt 使用 DynamoDB 锁 CommitHandler->>DynamoDB: acquire_lock(dataset_id) DynamoDB-->>CommitHandler: lock_acquired else 使用文件锁 CommitHandler->>ObjectStore: create_lock_file() ObjectStore-->>CommitHandler: lock_acquired end CommitHandler->>ObjectStore: write_manifest(version_n) ObjectStore-->>CommitHandler: write_success CommitHandler->>ObjectStore: update_latest_pointer() ObjectStore-->>CommitHandler: update_success alt 使用 DynamoDB 锁 CommitHandler->>DynamoDB: release_lock() else 使用文件锁 CommitHandler->>ObjectStore: delete_lock_file() end CommitHandler-->>Transaction: commit_success Transaction-->>Client: 返回新版本号

第一部分:Lance 的定位与应用场景

What - Lance 是什么?

定义:Lance 是一个 Apache 许可的、开源的列式数据格式和表格式(Lakehouse Format)。

核心特点:

  • 列式存储:按列而非行存储数据,适合分析型查询
  • 向量原生支持:内置向量索引和搜索能力
  • 多模态数据:支持图像、视频、音频、文本、向量等混合存储
  • ACID 事务:支持原子性更新和版本控制

Why - 为什么需要 Lance?

问题背景

传统数据格式存在的痛点:

功能需求 Parquet ORC Iceberg Lance
随机访问速度 ⭐⭐ ⭐⭐⭐⭐⭐
向量搜索支持
多模态支持
Schema 演化 困难 困难
版本控制 需要外部系统 需要外部系统

为什么选择 Lance?

  1. 100 倍随机访问速度提升:Parquet 为 100ns/随机访问,Lance 为 1ns,对于 ML 训练至关重要
  2. 向量搜索加速:内置 IVF-PQ、HNSW 等索引,毫秒级查询
  3. 零拷贝版本控制:无需数据复制即可实现版本管理和时间旅行
  4. 多模态一体化:在同一格式中存储结构化数据和非结构化数据

How - Lance 如何解决这些问题?

核心设计原则
python 复制代码
┌─────────────────────────────────────────────────────────┐
│         Lance 的四大设计支柱                               │
├─────────────────────────────────────────────────────────┤
│ 1️⃣ 高效的列式存储                                        │
│    → 通过多种编码(Bitpacking、Dict、RLE)最小化存储    │
│    → 支持快速列扫描和随机访问                            │
│                                                          │
│ 2️⃣ 向量原生能力                                         │
│    → 内置向量索引和量化技术                             │
│    → 支持混合搜索(向量+SQL)                            │
│                                                          │
│ 3️⃣ 灵活的版本管理                                       │
│    → Manifest + Fragment 设计                           │
│    → 无锁并发更新                                       │
│                                                          │
│ 4️⃣ 云原生架构                                          │
│    → 基于对象存储(S3/GCS/Azure)                       │
│    → 分布式计算友好                                     │
└─────────────────────────────────────────────────────────┘

第二部分:与其他格式的对比优势

Parquet 与 Lance 的差异

场景 1:机器学习训练

python 复制代码
# Parquet 的问题:需要顺序读取大部分数据
# 训练一个 1000 万行的数据集,但每次迭代只需要 100 行
# - 读取 Parquet:必须扫描整个文件,性能低

# Lance 的优势:支持随机行访问
dataset = lance.open("features.lance")
# 直接获取第 5000、8000、12000 行,无需全扫描
rows = dataset.take([5000, 8000, 12000])

场景 2:多模态存储

python 复制代码
# 需要存储:文本 + 图像向量 + 原始图像
import pandas as pd
import lance

df = pd.DataFrame({
    'text': ['A cat sleeping', 'A dog running', ...],
    'image_vector': [[0.1, 0.2, 0.3, ...], ...],  # 向量嵌入
    'image_blob': [image_bytes_1, image_bytes_2, ...],  # 原始图像
})

# Lance 原生支持这种混合存储
dataset = lance.write_dataset(df, "multimodal.lance")

场景 3:实时特征更新

python 复制代码
# 需要不断更新特征值,同时保持版本历史
# Iceberg 需要重写大量文件
# Lance 只需追加新 Fragment + 更新 Manifest

dataset = lance.open("features.lance")
dataset.add_column("new_feature", values)  # 原子性添加
# 自动版本控制,可回溯到任何历史版本

与 Iceberg 的差异

方面 Iceberg Lance
设计目标 数据湖、表管理 向量搜索、ML 工作流
向量支持 无原生支持 内置索引和量化
随机访问 需要额外优化 内置优化
学习曲线 陡峭 平缓
生态支持 更完善(Spark、Flink) 正在快速发展

第三部分:整体架构设计思想

分层架构模型

graph TB subgraph "应用层 Application" A1["Python API"] A2["Java API"] A3["SQL Interface"] end subgraph "查询引擎层 Query Engine" B1["DataFusion"] B2["Scanner"] B3["Index Planner"] end subgraph "数据管理层 Data Management" C1["Dataset"] C2["Transaction"] C3["Version Control"] end subgraph "索引加速层 Index Acceleration" D1["向量索引
IVF/HNSW"] D2["标量索引
BTree/Bitmap"] end subgraph "存储编码层 Storage & Encoding" E1["File Format"] E2["Compression"] E3["Encoding"] end subgraph "对象存储 Object Storage" F["S3/GCS/Local"] end A1 --> B1 A2 --> B1 A3 --> B1 B1 --> B2 B1 --> B3 B2 --> C1 B3 --> C1 C1 --> C2 C1 --> C3 C1 --> D1 C1 --> D2 D1 --> E1 D2 --> E1 E1 --> E2 E2 --> E3 E3 --> F

核心设计思想解析

1. 模块化与关注点分离

Lance 遵循严格的分层设计:

sql 复制代码
┌──────────────────────────────────────────────────────────┐
│  应用层:提供用户友好的 API(Python、Java、SQL)         │
├──────────────────────────────────────────────────────────┤
│  查询层:负责查询规划、优化、执行                        │
├──────────────────────────────────────────────────────────┤
│  数据层:Dataset、Transaction、版本管理                  │
├──────────────────────────────────────────────────────────┤
│  索引层:向量索引、标量索引的构建和查询                  │
├──────────────────────────────────────────────────────────┤
│  存储层:文件格式、编码、压缩、IO 调度                   │
├──────────────────────────────────────────────────────────┤
│  基础设施:对象存储、缓存、线程管理                      │
└──────────────────────────────────────────────────────────┘

优势

  • 每层可独立优化和扩展
  • 清晰的接口边界便于理解和维护
  • 便于新功能的添加(如新的索引类型)
2. 异步优先设计

Lance 大量使用 Rust 的异步编程(tokio 运行时):

rust 复制代码
// 不阻塞 IO 的写入
await dataset.write(data);

// 并发读取多个 Fragment
let futures = fragments
    .iter()
    .map(|f| f.read_async())
    .collect::<Vec<_>>();
futures::future::join_all(futures).await;

为什么?

  • I/O 不再阻塞计算线程
  • 单个线程可处理数千个并发请求
  • 在云环境中表现优异
3. 零拷贝与 Arrow 原生

Lance 与 Apache Arrow 深度集成:

rust 复制代码
// 数据从文件解码直接变成 Arrow RecordBatch
// 无需中间转换或拷贝
let batch: RecordBatch = decoder.decode().await?;

// RecordBatch 可直接传递给 NumPy、Pandas、Polars
// 共享内存地址,零拷贝
4. 索引透明化

索引对用户完全透明:

python 复制代码
# 用户编写简单的查询
results = dataset.search(query_vector, k=10)

# 内部自动选择最优索引
# - 如果有向量索引 → 使用 IVF/HNSW
# - 如果有标量索引 → 使用谓词下推
# - 否则 → 全表扫描

实战场景示例

场景 1:电商推荐系统

需求

  • 存储 1 亿个商品(ID、名称、描述、价格、分类)
  • 每个商品有 768 维向量嵌入
  • 需要基于向量的相似商品推荐
  • 需要基于价格、分类的标量过滤

Lance 解决方案

python 复制代码
import lance
import pandas as pd
from lance.vector_search import IVF_PQ

# 1. 初始数据加载
products = pd.read_csv("products.csv")
dataset = lance.write_dataset(products, "products.lance")

# 2. 创建向量索引加速搜索
dataset.create_index("vector", index_type=IVF_PQ(num_partitions=256))

# 3. 推荐查询(向量 + SQL)
query_vector = embed_text("高端运动鞋")
results = dataset.search(
    query_vector,
    k=100,
    where="price < 500 AND category == '鞋类'",  # 标量过滤
    metric="cosine"
)
# 毫秒级返回最相关的 100 个商品

为什么 Lance 优于 Elasticsearch?

  • 更紧凑的存储(编码优化)
  • 更快的向量搜索(PQ 量化)
  • 原生 SQL 支持(无需写 JSON 查询)
  • 更低的学习成本

场景 2:生物信息学分析

需求

  • 存储基因序列数据(超大 BLOB)
  • 存储 DNA 序列的向量化表示
  • 支持相似性搜索和统计分析

Lance 优势

python 复制代码
# 存储原始 DNA 序列 + 向量化表示
dna_data = pd.DataFrame({
    'gene_id': ['BRCA1', 'TP53', ...],
    'sequence': [dna_seq_1, dna_seq_2, ...],  # 超大字符串
    'embedding': [vec1, vec2, ...],  # 768 维向量
    'properties': [prop1, prop2, ...]  # 元数据
})

dataset = lance.write_dataset(dna_data, "dna.lance")

# 直接支持混合查询
results = dataset.search(
    query_embedding,
    k=10,
    where="properties.organism == 'human'"
)

核心工作流概览

一个完整的 Lance 使用周期

sequenceDiagram participant User participant Python API participant Dataset participant Index participant Storage User->>Python API: 1. 创建数据集 Python API->>Dataset: write_dataset(data) Dataset->>Storage: 写入文件 User->>Python API: 2. 创建索引 Python API->>Dataset: create_index() Dataset->>Index: 构建向量索引 Index->>Storage: 保存索引 User->>Python API: 3. 执行查询 Python API->>Dataset: search(query) Dataset->>Index: 使用索引加速 Index->>Storage: 读取数据 Python API-->>User: 返回结果 User->>Python API: 4. 更新数据 Python API->>Dataset: add_column()/update() Dataset->>Storage: 原子性提交 Dataset-->>User: 新版本号

关键设计决策总结

决策 原因 权衡
Rust 实现 性能、内存安全 开发效率稍低
Async First 高并发、云原生 编程复杂度增加
列式存储 扫描性能、压缩率 不适合行级更新
多种编码 适应不同数据类型 编码/解码开销
向量索引 ML 工作流必需 索引构建成本
零拷贝设计 减少内存占用 依赖 Arrow 兼容性

总结

Lance 通过以下创新解决了 AI 时代的数据存储难题:

  1. 性能优先:100 倍随机访问、毫秒级向量搜索
  2. 功能全面:向量、SQL、多模态在一个系统中
  3. 用户友好:自动索引选择、透明的优化
  4. 生产就绪:ACID 事务、版本控制、云支持

在接下来的章节中,我们将深入探讨 Lance 的内部实现细节,从项目结构、数据模型、文件格式、到索引和查询引擎,全面理解这个强大的系统是如何运作的。


关键概念速记

  • 列式存储:按列而非行组织数据
  • 向量索引:加速向量相似性搜索的数据结构
  • IVF-PQ:乘积量化的倒排文件,Lance 中常用的索引方式
  • Manifest:记录数据集版本信息的元数据文件
  • Fragment:数据集中的数据分片单位
  • 零拷贝:数据在内存中传递而无需复制
相关推荐
TracyCoder1232 小时前
微服务注册中心基础(一):AP架构原理
微服务·云原生·架构·注册中心
Kapaseker2 小时前
十年开发告诉你什么是“烂代码”
架构
Java烘焙师3 小时前
架构师必备:限流方案选型(原理篇)
架构·限流·源码分析
爱吃牛肉的大老虎9 小时前
网络传输架构之GraphQL讲解
后端·架构·graphql
Curvatureflight13 小时前
GPT-4o Realtime 之后:全双工语音大模型如何改变下一代人机交互?
人工智能·语言模型·架构·人机交互
t***D26415 小时前
云原生架构
云原生·架构
jinxinyuuuus15 小时前
局域网文件传输:P2P架构中NAT穿透、打洞与数据安全协议
网络协议·架构·p2p
六行神算API-天璇17 小时前
架构实战:打造基于大模型的“混合搜索”系统,兼顾关键词与语义
人工智能·架构
顾林海18 小时前
从0到1搭建Android网络框架:别再让你的请求在"路上迷路"了
android·面试·架构