返回
RSS Apache Iceberg Blog AI 逐段翻译 发布 2026-08-12 08:00 收录于 08-13

Apache Iceberg Variant类型:半结构化数据新解法

DataHot 速览

Apache Iceberg v3引入Variant类型,用于在单列中存储任意形状的半结构化数据,并以紧凑二进制形式保持原生类型。相比JSON字符串和固定扁平schema,Variant兼具灵活性与查询性能,且无需预定义schema和迁移。本文是该系列首篇,后续将介绍shredding技术。

为什么值得关注:湖仓从业者需关注Variant如何改善半结构化数据的存储与查询效率,影响表格式设计与引擎兼容策略。

本文目录 9 节
  1. Variant解决的问题🔗
  2. Apache Iceberg中的Variant🔗
  3. 存储方式🔗
  4. 跨引擎的读写🔗
  5. 使用Variant🔗
  6. 何时使用Variant🔗
  7. 下一步:shredding🔗
  8. 参与贡献🔗
  9. 资源🔗

译文

AI 逐段翻译

半结构化数据,例如字段随行而异的JSON类文档,始终难以适应围绕固定模式构建的表格式。Iceberg v3为这类数据添加了Variant类型:单个列可以容纳任意、不断演变的形状的值,以紧凑的二进制形式存储,引擎能一致地读写。

这是关于Apache Iceberg中Variant的系列文章的第一篇。它涵盖了Variant是什么、为什么存在,以及它如何融入Iceberg表。下一篇文章将介绍shredding,即将频繁访问的Variant字段存储为类型化、列式数据的技术。Variant存储在Parquet、Avro和ORC中;shredding目前仅适用于Parquet。

Variant解决的问题🔗

考虑形状随时间变化的事件数据:

{"event":"view","page":"/pricing","session":"s-8f21"}{"event":"signup","session":"s-8f21","plan":"pro","price":29.00}{"event":"view","page":"/docs","session":"s-3c07","referrer":{"source":"search","term":"iceberg variant"}}

两种传统方法可以处理这种情况,但它们都有缺点:

  • JSON存储为字符串。这种方法灵活,但读取单个字段意味着解析整个文本。JSON的类型系统也很薄弱:时间戳只是字符串,数字的精度不明确。
  • 僵硬的扁平化模式。这种方法查询速度快,但每个新字段都是一次模式迁移,稀疏或一次性的字段会浪费空间。

Variant像JSON一样灵活,但以紧凑、类型化的二进制形式存储数据。值保留其原生类型:时间戳保持时间戳,小数保持精确小数,而不是退化为JSON的字符串和数字。在值内部,字段名被收集到字典中,并通过ID引用,因此每次出现名称时都不会完整写出。无需预先声明模式,因此不同形状的文档可以共存于同一列中,新字段无需迁移。

Apache Iceberg中的Variant🔗

Variant在v3规范中被添加到Iceberg类型系统中。规范将其置于自己的类别中:variant是“既非原始类型也非嵌套类型”。它比原始类型更丰富,但没有像结构或列表那样固定声明的形状。

Variant值与JSON类似,但具有更广泛的原始类型,包括date、timestamp、timestamptz、binary和decimal。它还可以嵌套:

  1. 一个Variant数组是Variant值的有序集合。与Iceberg列表不同,其元素不受限于单一元素类型。
  2. 一个Variant对象是字符串键字段的集合,其值本身是Variant值。与Iceberg模式中的结构列不同,其字段不是一组固定的、命名的类型化列。

存储方式🔗

Iceberg没有为Variant定义自己的二进制编码。该类型及其编码来自Apache Parquet项目,Iceberg直接使用该编码,因此variant列映射到带有两个二进制字段的Parquetgroup:

optional group payload (VARIANT(1)) {
  required binary metadata;
  required binary value;
}
  • metadata保存值中使用的字段名字典,因此value字节通过整数ID引用每个名称,而不是重复名称字符串。
  • value保存编码数据:标量、数组或对象。数组和对象为每个元素存储一个field_offset(该元素值起始处的字节偏移量),对象还为每个字段存储一个field_id(元数据字典中的索引)。

Variant列本身像任何其他Iceberg列一样通过字段ID寻址,但其metadata和value子字段按名称访问,这对shredding很重要。

跨引擎的读写🔗

  • Apache Spark 4.0和4.1创建带有VARIANT列的表,并读写它们。读取在Spark 4.0上的Iceberg 1.10.0中实现;Iceberg 1.11.0添加了Spark 4.1,并在Spark 4.0和4.1上支持写入shredded Variant。
  • Apache Flink 2.1在Iceberg 1.11.0中增加了Variant支持。这仅涵盖未shredded的Variant;shredded写入支持已合并,预计在后续版本中发布。

由于这些引擎都使用相同的Iceberg Variant类型,一个引擎写入的值在另一个引擎中读取时是相同的。

Apache Arrow通过其规范扩展类型在内存中携带相同的Variant值arrow.parquet.variant,使引擎无需特殊处理即可交换它。

使用Variant🔗

使用Spark SQL,您可以将异构事件存储在单个VARIANT列中,并使用variant_get读回字段。Variant列需要Iceberg v3表:

-- Variant is a v3 type, so the table must be format version 3CREATETABLEevents(idBIGINT,payloadVARIANT)USINGicebergTBLPROPERTIES('format-version'='3');-- Insert events of different shapes into the same columnINSERTINTOeventsVALUES(1,parse_json('{"event": "login", "country": "US"}')),(2,parse_json('{"event": "purchase", "country": "UK", "amount": 99}')),(3,parse_json('{"event": "login"}'));-- Read fields out of the Variant with variant_get(column, path, type)SELECTid,variant_get(payload,'$.event','string')ASevent,variant_get(payload,'$.country','string')AScountry,variant_get(payload,'$.amount','int')ASamountFROMevents;

查询返回每行一个事件,字段缺失处为null:

ideventcountryamount
1loginUSnull
2purchaseUK99
3loginnullnull

第3行只携带event,因此country和amount读回为null。Variant不要求每行具有相同结构,因此异构事件可以存在于同一列中。

何时使用Variant🔗

当您无法控制数据形状,或者数据变化速度快于您想要演进模式的速度时,Variant是正确的选择:

  • 事件和点击流数据,其中每种事件类型携带不同的字段集,并且新字段随时间出现。
  • 应用程序和服务日志,具有结构化但异构的负载。
  • IoT和传感器遥测,其中每个设备型号报告自己的读数。
  • 第三方API响应和webhook,其模式由他人所有,可能随时更改。
  • 稀疏属性,否则会变成宽表,包含大部分为null的列。

Variant不是已知模式的替代品。当字段存在于每一行且您经常查询它时,常规的类型化列或用于固定嵌套形状的结构体更简单、更高效。常见模式是保留稳定、频繁查询的字段作为普通列,并将可变部分放入单个Variant列。

下一步:shredding🔗

到目前为止,Variant列是单个metadata + value对。读取一个字段意味着解码metadata字典,然后如果存在该字段,则从valueblob中反序列化它。这很灵活,但灵活性以性能为代价:由于整个文档驻留在单个value列,引擎无法像处理常规类型列那样,利用每字段的最小/最大统计信息来裁剪数据。碎片化解决了这个问题:它将频繁访问的字段存储为独立的、带类型的Parquet列,因此仅涉及这些字段的查询只读取这些列,并能利用它们的统计信息进行数据跳过,而不适合的字段则回退到无类型值。本系列的下一篇文章将解释碎片化的工作原理及其带来的优势。

参与贡献🔗

Iceberg中的Variant支持仍在发展中,欢迎贡献。 Variant跟踪问题(#10392)跟踪正在进行的工作,您可以通过邮件列表和Slack联系社区。

资源🔗

这篇内容对你有用吗?

反馈只用于改善内容筛选,不等同于收藏

分享这条资讯
分享海报
保存图片
iOS 也可以长按图片保存