本文是 Lance 表事务层的规范说明,重点阐明 MVCC 下的提交协议、事务类型、冲突检测与重试/重放机制。 核心要点:Lance 依赖对象存储原子原语(rename/put if not exists)实现并发提交下的单写成功语义。文档给出完整事务类型集合(append/delete/overwrite/merge…
Reprint Notice(转载提示):本文转载自 https://lance.org/format/table/transaction/。 原文标题:Transaction Specification。 原文语言:English。本文为中文翻译版本,英文原文已作为 content_en 保留。事务规范事务概览 Lance 实现了多版本并发控制(MVCC),为并发读取器和写入器提供 ACID 事务保证。 每次提交都会通过原子存储操作创建一个新的不可变表版本。 所有表版本都形成可序列化的历史记录,从而实现时间旅行和模式演化等功能。 事务是 Lance 中变化的基本单位。 事务描述了一组要自动应用以创建新表版本的修改。 事务模型通过乐观并发控制和自动冲突解决来支持并发写入。提交协议存储原语 Lance 提交依赖于底层对象存储提供的原子写入操作:rename-if-not-exists:仅当目标不存在时才自动重命名文件put-if-not-exists:仅当文件尚不存在时才以原子方式写入文件(也称为 PUT-IF-NONE-MATCH 或条件 PUT) 这些原语保证当多个写入器尝试同时创建相同的清单文件时,只有一个写入器成功。清单命名方案 Lance 支持两种清单命名方案:V1:{version}.manifest - 单调递增的版本号(例如,1.manifest、2.manifest)V2:{u64::MAX - version:020}.manifest - 反向排序的字典顺序(例如版本 1 的 18446744073709551614.manifest) V2 方案可以通过字典对象列表有效地发现最新版本。事务文件 事务文件存储每次提交尝试的序列化事务 protobuf 消息。 这些文件有两个用途:当并发事务已提交时,在提交重试期间启用清单重建通过描述执行的操作支持冲突检测提交算法 提交过程尝试使用上述存储原语以原子方式写入新的清单文件。 当并发写入器发生冲突时,系统会加载事务文件以检测冲突,并在可能的情况下尝试对事务执行 rebase。 如果原子提交失败,进程将使用更新的事务状态重试。 有关详细的冲突检测和解决机制,请参阅冲突解决 部分。事务类型 事务类型的权威规范在 protos/transaction.proto 中定义。 每个事务都包含一个 read_version 字段,指示构建事务的表版本, 唯一标识事务的 uuid 字段,以及指定以下事务类型之一的 operation 字段:Append 将新片段添加到表中而不修改现有数据。 片段 ID 不是在事务创建时分配的;它们是在清单构建期间分配的。message Append { // The new fragments to append. // // Fragment IDs are not yet assigned. repeated DataFragment fragments = 1; } Delete 使用删除向量将行标记为已删除。 可以更新片段(添加删除向量)或删除整个片段。 predicate 字段存储删除条件,启用并发事务的冲突检测。message Delete { // The fragments to update // // The fragment IDs will match existing fragments in the dataset. repeated DataFragment updated_fragments = 1; // The fragments to delete entirely. repeated uint64 deleted_fragment_ids = 2; // The predicate that was evaluated // // This may be used to determine whether the delete would have affected // files written by a concurrent transaction. string predicate = 3; } Overwrite 使用新数据、架构和配置创建或完全覆盖表。message Overwrite { // The new fragments // // Fragment IDs are not yet assigned. repeated DataFragment fragments = 1; // The new schema repeated lance.file.Field schema = 2; // Schema metadata. map<string, bytes> schema_meta…