当前位置: 首页 > news >正文

DeepSeek总结的parquet Variant “碎形化“技术

来源:https://github.com/apache/parquet-format/blob/master/VariantShredding.md

Variant 碎形化 (Shredding)

Variant 类型旨在高效存储和处理半结构化数据,即使面对异构值也是如此。查询引擎将每个 Variant 值以一种自描述格式编码,并将其作为包含valuemetadata二进制字段的 group 存储在 Parquet 中。由于数据通常是部分同构的,将某些字段提取到单独的 Parquet 列中以进一步提高性能是有益的。这个过程称为碎形化 (shredding)

碎形化使得能够利用 Parquet 的列式表示来实现更紧凑的数据编码、用于数据跳过的列统计信息以及部分投影。

例如,查询SELECT variant_get(event, '$.event_ts', 'timestamp') FROM tbl只需要加载event_ts字段,如果该列已被碎形化,则可以通过列式投影读取它,而无需读取或反序列化eventVariant 的其余部分。类似地,对于查询SELECT * FROM tbl WHERE variant_get(event, '$.event_type', 'string') = 'signup'event_type碎形化列的元数据可用于跳过数据并延迟加载 Variant 的其余部分。

Variant 元数据

无论 Variant 值是否被碎形化,Variant 元数据都存储在顶层 Variant group 的一个二进制metadata列中。

Variant 中的所有value列必须使用相同的metadata。Variant 的所有字段名,无论是否被碎形化,都必须存在于元数据中。

值碎形化

Variant 值存储在名为value的 Parquet 字段中。每个value字段可能有一个关联的碎形化字段,名为typed_value,当值与特定类型匹配时,该字段存储该值。当存在typed_value时,读取器必须根据此规范重构碎形化值。

例如,一个 Variant 字段measurement可以通过添加类型为int64typed_value作为长整型值进行碎形化:

required group measurement (VARIANT(1)) { required binary metadata; optional binary value; optional int64 typed_value; }

用于存储 variant 元数据和值的 Parquet 列必须按名称访问,而不是按位置。

一系列测量值34, null, "n/a", 100将存储为:

metadatavaluetyped_value
3401 00v1/空null34
null01 00v1/空00(null)null
“n/a”01 00v1/空13 6E 2F 61(n/a)null
10001 00v1/空null100

valuetyped_value都是用于编码单个值的可选字段。这两个字段中的值必须根据下表进行解释:

valuetyped_value含义
nullnull值缺失;仅对碎形化对象字段有效
非 nullnull值存在,可以是任何类型,包括 null
null非 null值存在,并且是碎形化类型
非 null非 null值存在,并且是部分碎形化的对象

value是一个对象且typed_value是一个碎形化对象时,该对象被称为部分碎形化。写入器不得生成valuetyped_value都非 null 的数据,除非 Variant 值是一个对象。

如果在需要值的上下文中缺少 Variant,读取器必须返回一个 Variant null (00):基本类型 0 (primitive) 和物理类型 0 (null)。例如,如果需要一个 Variant(如上面的measurement),并且valuetyped_value都为 null,则返回的value必须是00(Variant null)。

碎形化值类型

碎形化值必须使用以下 Parquet 类型:

Variant 类型Parquet 物理类型Parquet 逻辑类型
booleanBOOLEAN
int8INT32INT(8, signed=true)
int16INT32INT(16, signed=true)
int32INT32
int64INT64
floatFLOAT
doubleDOUBLE
decimal4INT32DECIMAL(P, S)
decimal8INT64DECIMAL(P, S)
decimal16BYTE_ARRAY / FIXED_LEN_BYTE_ARRAYDECIMAL(P, S)
dateINT32DATE
timeINT64TIME(false, MICROS)
timestamptz(6)INT64TIMESTAMP(true, MICROS)
timestamptz(9)INT64TIMESTAMP(true, NANOS)
timestampntz(6)INT64TIMESTAMP(false, MICROS)
timestampntz(9)INT64TIMESTAMP(false, NANOS)
binaryBINARY
stringBINARYSTRING
uuidFIXED_LEN_BYTE_ARRAY[len=16]UUID
arrayGROUP; 见下面的数组LIST
objectGROUP; 见下面的对象
基本类型

基本类型值可以使用上表中等效的 Parquet 基本类型作为typed_value进行碎形化。

除非该值作为对象被碎形化(参见对象),否则typed_valuevalue(但不能同时)必须非 null。

数组

数组可以通过为typed_value使用 3 级 Parquet 列表进行碎形化。

如果该值不是数组,则typed_value必须为 null。如果该值是数组,则value必须为 null。

列表element必须是一个 required group。elementgroup 可以包含valuetyped_value字段。当typed_value不存在或无法表示元素时,元素的value字段将元素存储为 Variant 编码的binary。当不将元素碎形化为特定类型时,可以省略typed_value字段。当将元素碎形化为特定类型时,可以省略value字段。但是,这两个字段中至少必须存在一个。

例如,一个tagsVariant 可以使用以下定义碎形化为一个字符串列表:

optional group tags (VARIANT(1)) { required binary metadata; optional binary value; optional group typed_value (LIST) { # 必须为 optional 以允许 null 列表 repeated group list { required group element { # 碎形化元素 optional binary value; optional binary typed_value (STRING); } } } }

数组的所有元素都必须存在(不能缺失),因为数组 Variant 编码不允许缺失元素。也就是说,typed_valuevalue(但不能同时)必须非 null。null 元素必须在value中编码为 Variant null:基本类型 0 (primitive) 和物理类型 0 (null)。

一系列tags数组["comedy", "drama"], ["horror", null], ["comedy", "drama", "romance"], null将存储为:

数组valuetyped_valuetyped_value...valuetyped_value...typed_value
["comedy", "drama"]null非 null[null, null][comedy,drama]
["horror", null]null非 null[null,00][horror, null]
["comedy", "drama", "romance"]null非 null[null, null, null][comedy,drama,romance]
null00(null)null
对象

对象的字段可以使用一个包含碎形化字段的 Parquet group 作为typed_value进行碎形化。

如果该值是对象,则typed_value必须非 null。如果该值不是对象,则typed_value必须为 null。如果typed_value为 null,读取器可以假定该值不是对象,并且typed_value字段值是正确的;也就是说,如果typed_value字段满足所需字段,读取器不需要读取value列。

typed_valuegroup 中的每个碎形化字段都表示为一个 required group,其中包含可选的valuetyped_value字段。当typed_value无法表示该字段时,value字段将该值存储为 Variant 编码的binary。这种布局使读取器能够基于valuetyped_value的字段统计信息跳过数据。当不将字段碎形化为特定类型时,可以省略typed_value字段。

部分碎形化对象的value列绝不能包含由typed_value中的 Parquet 列表示的字段(碎形化字段)。读取器可以始终假定数据被正确写入,并且typed_value中的碎形化字段不会出现在value中。因此,当一个字段同时定义在value和碎形化字段typed_value中时,读取结果可能不一致。

例如,一个 Variantevent字段可以使用以下定义碎形化event_type(string) 和event_ts(timestamp) 列:

optional group event (VARIANT(1)) { required binary metadata; optional binary value; # 一个 variant,预期是一个对象 optional group typed_value { # variant 对象的碎形化字段 required group event_type { # event_type 的碎形化字段 optional binary value; optional binary typed_value (STRING); } required group event_ts { # event_ts 的碎形化字段 optional binary value; optional int64 typed_value (TIMESTAMP(true, MICROS)); } } }

每个命名字段的 group 必须使用重复级别required

字段的valuetyped_value被设置为 null(缺失),以指示该字段在 variant 中不存在。要编码一个存在但值为 null 的字段,value必须包含一个 Variant null:基本类型 0 (primitive) 和物理类型 0 (null)。

当一个字段的valuetyped_value都非 null 时,引擎应该失败。如果引擎选择在这种情况下读取,则必须使用typed_value列。读取器可以始终假定数据被正确写入,并且只定义了valuetyped_value中的一个。因此,当valuetyped_value都定义时,读取结果可能与只需要其中一列的优化读取不一致。

下表显示了第一列中的一系列对象将如何存储:

Event 对象valuetyped_valuetyped_value.event_type.valuetyped_value.event_type.typed_valuetyped_value.event_ts.valuetyped_value.event_ts.typed_value注释
{"event_type": "noop", "event_ts": 1729794114937}null非 nullnullnoopnull1729794114937完全碎形化对象
{"event_type": "login", "event_ts": 1729794146402, "email": "user@example.com"}{"email": "user@example.com"}非 nullnullloginnull1729794146402部分碎形化对象
{"error_msg": "malformed: ..."}{"error_msg": "malformed: ..."}非 nullnullnullnullnull所有碎形化字段都缺失的对象
"malformed: not an object"malformed: not an objectnull不是对象(存储为 Variant 字符串)
{"event_ts": 1729794240241, "click": "_button"}{"click": "_button"}非 nullnullnullnull1729794240241字段event_type缺失
{"event_type": null, "event_ts": 1729794954163}null非 null00(字段存在,且为 null)nullnull1729794954163字段event_type存在且为 null
{"event_type": "noop", "event_ts": "2024-10-24"}null非 nullnullnoop"2024-10-24"null字段event_ts存在但不是时间戳
{ }null非 nullnullnullnullnull对象存在但为空
null00(null)null对象/值为 null
缺失nullnull对象/值缺失
无效:{"event_type": "login", "event_ts": 1729795057774}{"event_type": "login"}非 nullnullloginnull1729795057774无效: 碎形化字段存在于value
无效:{"event_type": "login"}{"event_type": "login"}null无效: 碎形化字段存在于value中,而typed_value为 null
无效:"a""a"非 nullnullnullnullnull无效:typed_value存在且value不是对象
无效:{}02 00(包含 0 个字段的对象)null无效: 对象的typed_value为 null

上表中的无效情况不得由写入器产生。当typed_value非 null 且包含碎形化字段时,读取器必须返回一个对象。

嵌套

与任何 Variantvalue字段关联的typed_value可以是任何碎形化类型,如上文各节所示。

例如,上面的event对象也可以将子字段碎形化为对象 (location) 或数组 (tags)。

optional group event (VARIANT(1)) { required binary metadata; optional binary value; optional group typed_value { required group event_type { optional binary value; optional binary typed_value (STRING); } required group event_ts { optional binary value; optional int64 typed_value (TIMESTAMP(true, MICROS)); } required group location { optional binary value; optional group typed_value { required group latitude { optional binary value; optional double typed_value; } required group longitude { optional binary value; optional double typed_value; } } } required group tags { optional binary value; optional group typed_value (LIST) { repeated group list { required group element { optional binary value; optional binary typed_value (STRING); } } } } } }

数据跳过

value始终为 null(缺失)时,typed_value列的统计信息可用于文件、行组或页面跳过。

当相应的value列全为 null 时,所有值必须是碎形化typed_value字段的类型。由于类型已知,与该类型值的比较是有效的。IS NULL/IS NOT NULLIS NAN/IS NOT NAN的过滤结果也是有效的。

与其他类型值的比较不一定有效,不应跳过数据。

Variant 的类型转换行为委托给处理引擎。例如,将字符串解释为时间戳可能取决于引擎的 SQL 会话时区。

重构碎形化 Variant

可以使用递归算法恢复未碎形化的 Variant 值,其中初始调用使用顶层 Variant group 字段调用construct_variant

defconstruct_variant(metadata:Metadata,value:Variant,typed_value:Any)->Variant:"""从 value 和 typed_value 构造 Variant"""iftyped_valueisnotNone:ifisinstance(typed_value,dict):# 这是一个碎形化对象object_fields={name:construct_variant(metadata,field.value,field.typed_value)for(name,field)intyped_value}ifvalueisnotNone:# 这是一个部分碎形化对象assertisinstance(value,VariantObject),"部分碎形化的值必须是一个对象"asserttyped_value.keys().isdisjoint(value.keys()),"对象键必须不重叠"# 合并碎形化字段和非碎形化字段# (字段 ID 和偏移量必须按对应字段名的顺序排列,# 按字典顺序排序(UTF-8 的无符号字节顺序))returnVariantObject(metadata,object_fields).union(VariantObject(metadata,value))else:returnVariantObject(metadata,object_fields)elifisinstance(typed_value,list):# 这是一个碎形化数组assertvalueisNone,"碎形化数组不得与 variant 值冲突"elements=[construct_variant(metadata,elem.value,elem.typed_value)foreleminlist(typed_value)]returnVariantArray(metadata,elements)else:# 这是一个碎形化基本类型assertvalueisNone,"碎形化基本类型不得与 variant 值冲突"returnprimitive_to_variant(typed_value)elifvalueisnotNone:returnVariant(metadata,value)else:# value 缺失returnNonedefprimitive_to_variant(typed_value:Any):Variant:ifisinstance(typed_value,int):returnVariantInteger(typed_value)elifisinstance(typed_value,str):returnVariantString(typed_value)...

向后和向前兼容性

碎形化是 Variant 的一个可选特性,读取器必须能够继续读取仅包含valuemetadata字段的 group。

不写入碎形化值的引擎必须能够根据此规范读取碎形化值,或者必须失败。

不同的文件可能包含冲突的碎形化 schema。也就是说,文件可能为同一个 Variant 包含具有不兼容类型的不同typed_value列。可能无法推断或指定一个单一的碎形化 schema,使得无需将值重构为 Variant 即可读取表的所有 Parquet 文件。

http://www.cnnetsun.cn/news/2939007.html

相关文章:

  • C#个人学习笔记之 数组的介绍--006
  • 酒店预订数据的探索性分析实战:EDA与可视化深度指南
  • MPC8533E嵌入式开发实战:PIC中断控制器与I2C总线驱动详解
  • 本地素材管理工具的技术架构启示:从Eagle的插件系统到AI能力的边缘集成
  • 终极免费方案:3分钟将Windows电脑变成专业无线共享中心
  • AI时代未来急需的四个岗位
  • NSK滚珠丝杠RNFTL1404A3.5S技术规格手册
  • 用目标传播训练硬激活神经网络:原理与PyTorch实操
  • 【Java零基础30天挑战·Day9】Java三大修饰符:public、private、protected,一文彻底搞懂访问控制
  • 如何构建抖音直播数据采集系统:开源工具深度解析与应用实践
  • 2026有孵化器EMBA中立测评:理性选型避坑指南
  • 【图像加密】基于matlab无限变换和闭环控制扩散的图像加密算法加密彩色图像【含Matlab源码 15631期】
  • Nucleus Co-Op终极指南:一台电脑实现4人分屏游戏的完整解决方案
  • GTA5线上小助手:你的洛圣都终极效率提升指南
  • GZDOOM联机模组避坑指南:如何快速判断你的WAD/PK3文件能不能多人玩
  • NXP EdgeLock Enclave HSM API实战:安全数据存储与设备认证详解
  • 2026年实测10款降AI率网站推荐:免费与付费全对比,毕业论文淡化AIGC痕迹必看
  • Parsec VDD虚拟显示器终极指南:如何免费扩展Windows显示系统
  • 别再只盯着UI了!聊聊HCI领域里那些容易被忽略的宝藏岗位(附技能树)
  • MPC866 PowerPC异常处理与缓存管理:原理、实践与优化
  • MPC866 SCC串行通信控制器:架构、寄存器配置与缓冲区管理实战
  • 如何快速免费打造你的专属Markdown编辑体验:Typora橙心主题终极指南
  • 【图像加密】无限变换和闭环控制扩散的图像加密算法加密彩色图像【含Matlab源码 15631期】
  • GIS工程师的遥感+机器学习实战路径:从数据物理层到端到端部署
  • Kimi K2.6快速 LeetCode 3260. 找出最大的 N 位 K 回文数 Rust实现
  • 影刀RPA进阶教程_定时任务的正确配置姿势单次循环多任务与故障恢复
  • 深入解析PXD10电源管理模式:从基础原理到低功耗设计实践
  • 三步掌握lilToon卡通渲染的终极实战指南
  • 跨境多账号新环境从零搭建完整配置指南
  • 如何用自然语言控制电脑?UI-TARS桌面助手给你答案