Delta Lake 表特性介绍
作者:Nick Karpov
Delta Lake 事务协议的最初设计规定了两个整数,称为“协议版本”,用于跟踪读写器特性。协议版本允许 Delta Lake 客户端通过检测不兼容的协议版本来安全地处理表,从而帮助管理 Delta Lake 协议中特性的演进。
这个设计自 Delta Lake 诞生以来一直运行良好,但并非没有复杂性和约束。在基于整数的协议版本控制方案中,每个版本号都捆绑了多个特性,并且跨版本号的特性是累积的。因此,为了在特定协议版本中获得新特性,用户和开发人员不仅必须启用或实现给定版本中的所有特性,而且还要实现之前所有版本中的所有特性!
版本控制对于像 Delta Lake 这样的项目来说是一个特别困难的问题,它必须同时考虑用户、管理员和开发人员,跨多个堆栈,每个堆栈都有自己的约束集——所有这些都在一个开放协议中进行。这就是为什么我们非常高兴地推出 Delta Lake 的新表兼容性管理方法——表特性,它取代了基于整数的协议读写器版本作为 Delta Lake 表的主要版本控制方案。
什么是 Delta Lake 表特性?
Delta Lake 表特性是一种离散的、基于特性的兼容性方案,它取代了传统的基于连续整数的协议版本控制方案,成为 Delta Lake 表和客户端的主要特性管理机制。
借助表特性,用户可以根据他们使用的特性(而不是版本号)来定义他们的表,这大大简化了表的可用性、维护和升级。开发人员现在还可以选择只开发其用例所需的特性,而不必实现所有先前版本号中捆绑的所有特性。
Delta Lake 表特性如何工作?
为了将 Delta Lake 表特性与旧版协议版本控制方案关联起来,对表特性语义的支持本身被视为最终协议版本的一部分,即读取器版本 3 和写入器版本 7。这些是 Delta Lake 协议引入的最后协议版本,所有后续特性现在都通过各自的特性标志显式启用。
作为 Delta Spark 用户
从协议版本到表特性的迁移旨在对用户透明。现有表无需显式升级即可受益,因为匹配和处理旧版协议版本与表特性的逻辑由客户端完成。您仍然可以选择手动升级协议版本。
为了突出表特性在实践中如何工作,让我们看看使用最新发布的 Delta Spark 连接器 v2.4.0 的表的演变。支持表特性的最早 Delta Spark 版本是 v2.3.0。
创建一个新表会在 Delta 日志中添加以下协议操作
CREATE TABLE t1 (id INT) USING delta
...
{"protocol":{"minReaderVersion":1,"minWriterVersion":2}}
由于在创建过程中没有使用或显式启用像变更数据流 (Change Data Feed) 或删除向量 (Deletion Vectors) 这样的表特性,因此当前表状态假定最低可能的读写器版本。
让我们启用变更数据流并观察日志的变化。
ALTER TABLE t1 SET TBLPROPERTIES (delta.enableChangeDataFeed = true)
...
{"protocol":{"minReaderVersion":1,"minWriterVersion":4}}
我们可以看到,启用变更数据流导致 Delta Spark 将表升级到包含变更数据流特性的协议版本 (minWriterVersion 4)。旧版协议版本到特性的映射可在文档中找到。
现在让我们看看当我们启用 Delta Lake 中可用的最新特性之一,即删除向量时,客户端添加到 Delta 日志的协议操作。
ALTER TABLE t1 SET TBLPROPERTIES (delta.enableDeletionVectors = true)
...
{
"protocol": {
"minReaderVersion": 3,
"minWriterVersion": 7,
"readerFeatures": [
"deletionVectors"
],
"writerFeatures": [
"deletionVectors",
"checkConstraints",
"generatedColumns",
"invariants",
"changeDataFeed",
"appendOnly"
]
}
}
这次我们看到 Delta Spark 已将表升级到 "minReaderVersion": 3
和 "minWriterVersion": 7
,这些是启用对新表特性版本控制方案(删除向量所属)支持的最终旧版协议版本。
我们还看到协议操作中出现了新字段 readerFeatures
和 writerFeatures
,它们分别包含适用于表的读取和写入的特性列表。
我们没有显式添加列表中的许多特性。这是因为它们被捆绑到我们在启用变更数据流时继承的旧版协议版本升级中。下图匹配上面列出的特性,并捕获每个特性来自哪个协议版本。
有些特性同时出现在协议操作的 readerFeatures
和 writerFeatures
中,而其他特性只出现在两者之一。这是因为并非所有特性都需要更改写入和读取行为。下表总结了哪些特性适用于 readerFeatures
和 writerFeatures
之一或两者。
特性 | 名称 | 读取器或写入器? |
---|---|---|
仅追加表 | appendOnly | 仅写入器 |
列不变性 | invariants | 仅写入器 |
CHECK 约束 | checkConstraints | 仅写入器 |
变更数据流 | changeDataFeed | 仅写入器 |
生成列 | generatedColumns | 仅写入器 |
列映射 | columnMapping | 读取器和写入器 |
标识列 | identityColumns | 仅写入器 |
删除向量 | deletionVectors | 读取器和写入器 |
作为 Delta Lake 客户端开发人员
Delta Lake 客户端的开发人员应实现 PROTOCOL、设计文档中详述的表特性语义,将其支持的最低协议版本升级到读取器版本 3、写入器版本 7,并实现所需的特性。
对于尚未实现对先前协议版本支持的新客户端,声明支持最新协议版本并添加对 appendOnly
和 invariants
特性的支持就足够了。这两个特性是早期协议版本(读取器版本 1 和写入器版本 2)所要求的。
客户端必须维护其支持的特性列表,并实现正确的逻辑来确认客户端是否可以读取和/或写入 Delta Lake 表。例如,通过启用带有协议读取器版本 3 和写入器版本 7 的表特性,您可以选择性地开发像变更数据流这样的特性,而无需同时开发写入器版本 2 到 4 之间的所有特性。
下面是 表特性设计文档中关于开发 Delta Lake 客户端时应遵循的表特性语义的摘录
- 当读取表时,读取器比较以下条件:
- 表的
readerFeatures
是客户端supportedReaderFeatures
的一个子集
- 表的
- 当写入表而不读取数据时,写入器比较以下条件:
- 表的
writerFeatures
是客户端supportedWriterFeatures
的一个子集
- 表的
- 当读取和写入表时,写入器比较以下条件:
- 表的
readerFeatures
是客户端supportedReaderFeatures
的一个子集,并且 - 表的
writerFeatures
是客户端supportedWriterFeatures
的一个子集
- 表的
希望继续支持旧版表版本(读取器版本 < 3 和写入器版本 < 7)的 Delta Lake 客户端应使用旧版协议版本与其捆绑的表特性映射来透明地确定客户端必须支持哪些表特性才能安全使用该表。
您可以参考 Delta Spark 连接器中的参考实现此处。
下一步
表特性支持目前仅在参考 Delta Spark 连接器中提供。Delta Standalone 和其他非 Spark 客户端对表特性的支持目前正在进行中。
结论
在这篇文章中,我们介绍了表特性,它取代了 Delta Lake 协议中基于整数的旧版协议版本控制方案。表特性由最终协议版本(读取器版本 3 和写入器版本 7)启用,这些版本将不再用于管理或引入 Delta Lake 协议的新特性。
用户和管理员现在可以只启用他们需要的特性,而无需继承读取器版本 3 和写入器版本 7 之前捆绑到特定协议版本的所有特性。这通过使表特性明确且显式列出,消除了对任意捆绑许多特性的晦涩数字协议版本的依赖,从而简化了表管理生命周期。
开发人员现在也可以实现 Delta Lake 客户端,以仅支持他们需要的特性。通过声明与启用表特性的协议版本(读取器版本 3、写入器版本 7)的兼容性,开发人员现在可以更清晰地表达客户端支持哪些特性,同时消除实现可能不需要的特性的负担,就像旧版基于整数的协议版本控制方案一样。
表特性标志着 Delta Lake 协议的一个重要里程碑,并突出了协议适应不断变化的需求的创新速度和适应性。我们很高兴看到这项特性使创新速度进一步加快!