Apache Iceberg 中的 Variant 类型:解... 笔记

Apache Iceberg 中的 Variant 类型:解析如何将杂乱的 JSON 转化为快速分析

每位数据工程师都会遇到包含 JSON 列的表,这是数据景观中既宝贵又令人头疼的部分。这些列承载着原始真相,但由于字符串解析,会带来显著的查询性能开销。Apache Iceberg v3 引入了 Variant 类型以解决这一问题,在提供类 JSON 灵活性的同时,实现接近类型化列的性能。这一成果是通过一种称为“解构(shredding)”的技术实现的。在 Variant 出现之前,存在两种糟糕的选项。将 JSON 存储为字符串易于摄入,但读取缓慢,因为每次查询都需要解析整个字符串,且存储冗长。另一种选择是将 JSON 扁平化为类型化列,这使查询变快,但因模式不稳定和频繁迁移而带来运维痛苦。许多团队被迫同时维护这两种方式,增加了复杂性却未带来新信息。Variant 将上述选项合并为一种灵活且快速的列。它是一种用于行内结构可变值的类型,支持对象、数组以及日期、小数等基础类型。关键在于,Variant 值以二进制编码存储,而非文本,利用 Apache Parquet 标准。该编码将值分为元数据部分(字段名的字典)和值部分,高效存储基础类型,并支持直接跳转到特定字段。虽然二进制编码优于字符串存储,但尚未实现完整的列式性能,因为 Parquet 将 Variant 值视为不透明的数据块。这种不可见性阻碍了列式读取、压缩和剪枝等关键优化。解构通过使文件格式能够识别内部结构来解决这一问题。解构的工作原理是:在写入时,引擎识别 Variant 值中一致出现的字段,并将其作为独立的、类型化的 Parquet 列存储。例如,常见字段如 user_id 和 event_type 被提取到各自的列中,而较少出现或变化的字段则保留在残差二进制列中。这类似于邮务员将常见文件(发票、标签)单独归档,而其他物品仍保留在原信封中。从机制上看,解构为每个常见字段创建一对列:typed_value 和 value。typed_value 列在字段匹配预期类型时存储该字段,从而受益于所有列式优化。value 列作为后备,当字段不匹配预期类型时,以二进制 Variant 形式存储该字段。这确保了数据完整性,同时大幅提升了常见字段的查询速度。