理解 Lance 文件格式版本化:版本号体系、别名解析与跨版本兼容实践
2026/9/17 21:21:44 网站建设 项目流程

理解 Lance 文件格式版本化:版本号体系、别名解析与跨版本兼容实践

【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance

Lance 采用单一版本号同时约束文件格式与编码策略,新版本通常带来更好的性能与压缩率,但可能无法被旧版 Lance 读取。本文以 docs/src/format/file/versioning.md 为核心,结合lance-filecrate 的版本解析实现,系统讲解 major/minor 版本的语义分工、stable/next别名解析机制、各版本的能力边界,以及混合版本部署场景下的兼容性注意事项,帮助你为生产数据集选择正确的版本策略。

版本号体系:major 与 minor 的语义分工

Lance 文件格式使用一个版本号同时表达两层含义:

  • major 版本变更:表示文件格式本身发生了结构性修改(例如 0.1 → 2.0 移除了 row group、引入对 list/fixed size list/primitive 的 null 支持);
  • minor 版本变更:表示仅编码策略(encoding strategy)发生变化,文件格式骨架不变。

这种设计的直接后果是:新版本通常性能与压缩更好,但旧版本的 Lance 可能无法读取。因此版本号不仅是格式标识,更是一份兼容性契约。

从源码结构看,版本处理被拆成两层类型,定义在 rust/lance-file/src/version.rs:

  • LanceFileVersion(调用方请求层):包含LegacyV2_0V2_1V2_2StableNextV2_3七个取值,其中StableNext是发布选择器(release selector),在分派前会被解析为具体版本,从不持久化
  • ConcreteFileVersion(持久化层):包含V1V2_0V2_1V2_2V2_3五个精确版本。该类型故意没有排序关系,因为"格式能力并不由发布顺序隐含"(见 version.rs)。

支持的版本一览

当前 Lance 支持以下版本取值(来自 versioning.md 原始表格):

VersionMinimal Lance VersionMaximum Lance VersionDescription
0.1Any0.34 (write)最初的 Lance 格式。已不可写。
2.00.16.0AnyLance 文件格式重构:移除 row groups,为 list、fixed size list 和 primitives 引入 null 支持
2.10.38.1Any增强整数与字符串压缩,支持 struct 字段的 null,提升嵌套字段的随机访问性能
2.2NoneAny支持更新的嵌套类型/编码能力(包括 map 支持)及 2.2 时代的存储特性
2.3 (unstable)NoneUnspecified引入 sparse structural pages 及其他实验性编码
legacyN/AN/A0.1 的别名
stableN/AN/A当前 Lance 版本中新数据集默认版本号
nextN/AN/A当前 Lance 版本中最新的不稳定版本号

需要注意表格中的None含义:2.1、2.2 的 "Minimal Lance Version" 列,其约束体现在编码能力层面而非发布门槛——2.1 及以上版本可被旧 reader 部分读取的边界由实际数据模式决定,这也是下文"兼容性注意事项"章节存在的原因。

stable 与 next:别名解析机制

stablenext不是固定版本,而是由你正在使用的具体 Lance 发布版解析。在 rust/lance-file/src/version.rs 中可以看到当前发布策略:

/// Resolve the current stable release policy to an exact file version. pub const fn stable_file_version() -> ConcreteFileVersion { ConcreteFileVersion::V2_2 } /// Resolve the current next release policy to an exact file version. pub const fn next_file_version() -> ConcreteFileVersion { ConcreteFileVersion::V2_3 }

LanceFileVersion::resolve()将选择器映射为精确版本:Stable → V2_2Next → V2_3,且V2_2LanceFileVersion#[default]取值(version.rs)。对应的单元测试selector_resolution_is_exact直接断言了这一映射关系。

这种"运行时解析"意味着:同一段代码在不同 Lance 版本下,stable可能指向不同格式。因此文档建议——在一次格式 rollout(例如 2.3 发布)期间,优先使用显式版本固定,以保证跨环境行为确定性。

另一个值得注意的细节:manifest 与别名的边界。ConcreteFileVersion::from_manifest_string明确拒绝legacy0.3stablenext等别名,因为数据集 manifest 中只存储规范的精确版本字符串(version.rs)。别名仅是调用层的便利设施,不会落到磁盘上。

不稳定版本(2.3 / next):仅供实验与基准测试

任何被显式标记为 unstable 的版本(包括当前的 2.3 格式与next别名)都不应用于生产场景。原因是:

  • 不稳定格式没有兼容性保证;
  • 破坏性的编码变更可能导致一个 Lance build 写入的文件无法被后续 build 读取;
  • 它们只应用于试验与基准测试即将到来的特性。

源码中这一判断收敛为ConcreteFileVersion::is_unstable(),当前实现为matches!(self, Self::V2_3)(version.rs)。LanceFileVersion::is_unstable()则先resolve()再委托给精确版本的判定——也就是说,next别名是否不稳定,完全取决于当前发布策略把它解析到什么版本。

如何在代码中指定文件版本

Python:创建数据集时指定

在 Python API 中,可以通过write_datasetdata_storage_version参数控制写入文件使用的格式版本。仓库的基准测试代码给出了实际用法(python/python/benchmarks/test_random_access.py):

dsv1 = lance.write_dataset(tab, "/tmp/lineitem.lancev1", data_storage_version="2.0") dsv2 = lance.write_dataset(tab, "/tmp/lineitem.lancev2", data_storage_version="2.1")

TPC-H 基准数据生成脚本也使用同样的方式批量产出 2.0/2.1 版本的数据集(python/python/ci_benchmarks/datagen/lineitems.py),可用于跨版本读写对比。

Python:merge_insert 时固定写入版本

MergeInsertBuilder.data_storage_version(version)可精确指定该操作写入文件的版本(python/python/lance/dataset.py):

dataset.merge_insert("id").data_storage_version("2.2")

该参数接受"2.0""2.1""2.2""2.3""stable""next";若省略则沿用数据集默认写入版本。文档明确说明 V1/V2 跨系列目标会被拒绝。

Python:底层 LanceFileWriter 的 version 参数

更底层地,lance_file::LanceFileWriter构造函数的version参数直接接受字符串并解析为LanceFileVersion,随后resolve()得到精确版本再分派给对应 writer(python/src/file.rs):

let version = version .map(|value| value.parse::<LanceFileVersion>()) .transpose() .infer_error()? .unwrap_or_default() .resolve();

FromStr实现(version.rs)显示,"2.0""0.3"都被接受为 V2.0,"0.1""legacy"均映射为 Legacy——历史别名的兼容保留。

版本号在磁盘上的编码方式

文件版本最终以(major, minor)数字对与魔数形式落在磁盘的不同位置,源码中给出了完整的编码/解码对应关系(version.rs):

版本Manifest 字符串DataFile 元数据 (major, minor)标准文件 footer (major, minor)嵌入式/mini-lance footer
0.1 (V1)"0.1"(0, 0..=2)(0, 0..=2)(0, 2)
2.0 (V2_0)"2.0"(2, 0)(0, 3)(0, 3)(2, 0)
2.1"2.1"(2, 1)(2, 1)(2, 1)
2.2"2.2"(2, 2)(2, 2)(2, 2)
2.3"2.3"(2, 3)(2, 3)(2, 3)

几个值得留意的实现细节:

  • V2.0 存在两种合法表示:标准文件 writer 写入(0, 3),而 self-described 与 mini-lance writer 写入(2, 0),解码时两者都接受;
  • legacy manifest 可能省略版本字段而解码为(0, 0),因此历史解码器接受的所有 V1 数字对(0, 0..=2)依然有效;
  • 魔数b"LANC"与 major/minor 一起写在 Lance 文件末尾(如versions/1.version),常量定义在 rust/lance-file/src/format.rs(MAJOR_VERSION=0, MINOR_VERSION=2, MAGIC=b"LANC");
  • 每类 writer 的编码、元数据收尾、投影逻辑均按ConcreteFileVersion分派到v1v2_0v2_1v2_2v2_3五个模块,见 rust/lance-file/src/versions/mod.rs 与对应的 rust/lance-file/src/versions/ 目录结构。

兼容性注意事项:编码缺陷与混合版本部署

稳定格式承载兼容性保证,但某些数据模式曾暴露编码器缺陷,需要变更编码来修复。包含这些模式、由修复后编码器写出的文件,无法被修复前的 reader 读取。以下场景供运行混合版本部署的运维人员确定所需的最低 reader 版本。

FixedSizeList 全 null 内部值(Lance 11.1.0)

  • 受影响格式:2.1 及以后。
  • 触发场景FixedSizeList列中每个内部值(而非外层 list 项本身)都为 null。例如FixedSizeList<nullable Float32, dim=4>,两行外层记录对应的 8 个 Float32 值全部为 null。
  • 缺陷 writer(Lance < 11.1.0):编码器向 FullZip 页面布局写入bits_per_value=0。任意版本的 reader 都会拒绝这类页面并报错,因此无论 reader 版本如何,数据都不可读。
  • 修复 writer(Lance ≥ 11.1.0):编码器为 null 内部值存储逐行有效性字节,产生bits_per_value > 0。修复后的 reader(Lance ≥ 11.1.0)也能解码旧的缺陷页面,因此 11.1.0 之前写入的旧文件在升级后即可正常读取。
  • 前向兼容性:包含该模式、由 Lance ≥ 11.1.0 写出的文件无法被 Lance < 11.1.0 读取——旧 reader 会在 FSL descriptor 中遇到Compression::Constant内部编码时 panic 而非返回错误。
  • 新文件的最低 reader 版本:Lance 11.1.0。

这一案例说明:版本号表只描述"格式能力",而真实的数据模式可能触发个别版本的边界行为,升级部署时务必以这类清单为准进行验证。

版本选型与迁移实践建议

综合文档与源码,给出可落地的版本策略:

  1. 新数据集默认跟随stable:它由当前 Lance 发布版的发布策略决定,不必显式指定;
  2. 跨环境/多版本部署中显式固定版本:一次格式 rollout 期间(如 2.3 发布后),不同环境的stable可能解析到不同格式,应显式写入具体版本(如"2.2")保证确定性;
  3. 禁止在生产使用 unstable 格式:2.3 与next仅用于试验与基准测试,评估新编码(如 sparse structural pages)的性能与压缩收益,但不承载兼容性承诺;
  4. 升级前检查兼容性清单:特别是包含全 null 内部值 FixedSizeList 等边界数据模式的数据集,确认部署中所有 reader 均达到最低版本要求;
  5. 用仓库基准验证跨版本读写:可参考 python/python/benchmarks/test_random_access.py 与 python/python/ci_benchmarks/datagen/lineitems.py 中按版本批量生成数据集的方式,对目标版本做随机访问与读写基准对比后再切换默认版本。

版本策略的本质是"性能/压缩收益"与"兼容性成本"的权衡:新格式带来更好的编码,但也约束了 reader 的最低版本。理解版本号语义、别名解析机制与已知兼容性边界,是安全使用 Lance 多版本生态的前提。

【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询