理解 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(调用方请求层):包含Legacy、V2_0、V2_1、V2_2、Stable、Next、V2_3七个取值,其中Stable与Next是发布选择器(release selector),在分派前会被解析为具体版本,从不持久化;ConcreteFileVersion(持久化层):包含V1、V2_0、V2_1、V2_2、V2_3五个精确版本。该类型故意没有排序关系,因为"格式能力并不由发布顺序隐含"(见 version.rs)。
支持的版本一览
当前 Lance 支持以下版本取值(来自 versioning.md 原始表格):
| Version | Minimal Lance Version | Maximum Lance Version | Description |
|---|---|---|---|
| 0.1 | Any | 0.34 (write) | 最初的 Lance 格式。已不可写。 |
| 2.0 | 0.16.0 | Any | Lance 文件格式重构:移除 row groups,为 list、fixed size list 和 primitives 引入 null 支持 |
| 2.1 | 0.38.1 | Any | 增强整数与字符串压缩,支持 struct 字段的 null,提升嵌套字段的随机访问性能 |
| 2.2 | None | Any | 支持更新的嵌套类型/编码能力(包括 map 支持)及 2.2 时代的存储特性 |
| 2.3 (unstable) | None | Unspecified | 引入 sparse structural pages 及其他实验性编码 |
| legacy | N/A | N/A | 0.1 的别名 |
| stable | N/A | N/A | 当前 Lance 版本中新数据集默认版本号 |
| next | N/A | N/A | 当前 Lance 版本中最新的不稳定版本号 |
需要注意表格中的None含义:2.1、2.2 的 "Minimal Lance Version" 列,其约束体现在编码能力层面而非发布门槛——2.1 及以上版本可被旧 reader 部分读取的边界由实际数据模式决定,这也是下文"兼容性注意事项"章节存在的原因。
stable 与 next:别名解析机制
stable与next不是固定版本,而是由你正在使用的具体 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_2、Next → V2_3,且V2_2是LanceFileVersion的#[default]取值(version.rs)。对应的单元测试selector_resolution_is_exact直接断言了这一映射关系。
这种"运行时解析"意味着:同一段代码在不同 Lance 版本下,stable可能指向不同格式。因此文档建议——在一次格式 rollout(例如 2.3 发布)期间,优先使用显式版本固定,以保证跨环境行为确定性。
另一个值得注意的细节:manifest 与别名的边界。ConcreteFileVersion::from_manifest_string明确拒绝legacy、0.3、stable、next等别名,因为数据集 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_dataset的data_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分派到v1、v2_0、v2_1、v2_2、v2_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。
这一案例说明:版本号表只描述"格式能力",而真实的数据模式可能触发个别版本的边界行为,升级部署时务必以这类清单为准进行验证。
版本选型与迁移实践建议
综合文档与源码,给出可落地的版本策略:
- 新数据集默认跟随
stable:它由当前 Lance 发布版的发布策略决定,不必显式指定; - 跨环境/多版本部署中显式固定版本:一次格式 rollout 期间(如 2.3 发布后),不同环境的
stable可能解析到不同格式,应显式写入具体版本(如"2.2")保证确定性; - 禁止在生产使用 unstable 格式:2.3 与
next仅用于试验与基准测试,评估新编码(如 sparse structural pages)的性能与压缩收益,但不承载兼容性承诺; - 升级前检查兼容性清单:特别是包含全 null 内部值 FixedSizeList 等边界数据模式的数据集,确认部署中所有 reader 均达到最低版本要求;
- 用仓库基准验证跨版本读写:可参考 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),仅供参考