Apache Doris 高性能 Open Lake Variant 读写技术解析(含对比数据)

导读:Apache Doris 5.0 多模湖仓预告(5)|技术解读篇:面对日志、事件、AI 调用等不断变化的半结构化数据,如何既保留灵活结构,又实现高效查询?从 Doris 4.2 开始,IcebergPaimon 中的 Variant 数据可以直接高效读写,并通过按需读取、拆列优化和向量化计算提升查询效率。本轮测试中,相比 Spark,Doris 冷跑综合加速 15.10×、热跑 19.14×;拆列场景热跑综合加速达 58.64×,部分 Paimon 测试超过 106×。本文将介绍这些性能提升背后的关键技术。

作者:李文强,SelectDB 多模湖仓负责人

一、Variant:让变化的数据保留类型

一张事件表,今天多了设备属性,明天增加模型调用信息;同一个 amount,有的记录是数字,有的却是字符串。这类数据既要保留不断变化的结构,又要接受 SQL 过滤、聚合和关联。Variant 正是为这样的半结构化数据提供类型化表达。

Doris 4.2 开始,IcebergPaimonVariant 高效读写成为 Doris 湖仓能力的一部分。用户可以直接查询湖表中的半结构化数据,按嵌套字段进行过滤、聚合和关联,并将 Variant 值写回湖表。下文将介绍这些能力背后的类型表示、读取优化与计算机制。

理解 Variant,需要沿着三个层次往下看:它能表达什么值,文件怎样保存这些值,Doris 怎样把这些值交给计算算子

Variant 适合保存日志上下文、业务事件扩展属性、设备参数,以及 AI 应用中的调用信息、评测属性等半结构化内容。不同记录可以具有不同字段,同一路径也可以保存不同类型的值。

先把表结构与值的结构分开看。假设有一张 events 事件表,包含 id BIGINTpayload VARIANT 两列:id 保存事件编号,payload 保存事件的扩展内容。payload 只是本文示例中用户自定义的列名,VARIANT 才是这一列的数据类型;这个列名也可以换成 attributes 或其他名称。

一行的 payload 对应一个 Variant 值。这个值可以是数字、字符串等标量,也可以是由“字段名 → 子值”组成的对象,或者由多个元素组成的数组;对象和数组还可以继续嵌套。为便于阅读,下方示例用 JSON 写法展示逻辑内容,每行代码对应一条记录中的 payload 值;实际存储使用二进制编码,第二章会进一步展开。

例如,下列两个逻辑值可以出现在同一列中:

{"region":"west","amount":120,"device":{"os":"linux"}}
{"region":"east","amount":"unknown","tags":["new","app"]}

第一行的根值是一个对象:region 对应字符串 "west"amount 对应数字 120device 对应一个嵌套对象,其中的 os 对应字符串 "linux"。第二行则没有 device,增加了数组字段 tags,并把 amount 保存为字符串。这些差异都发生在同一列的不同值内部。

variant-logical-structure.png

图 1|从 events 表中的 payload 列,展开到一个 Variant 值的嵌套字段。图示表达逻辑结构;第二章再介绍实际的二进制编码。

后文的 payload['region'] 表示从这一列的值中取出 region 字段,payload['device']['os'] 则沿 device → os 逐层取值。例如,对第一行执行 CAST(payload['device']['os'] AS STRING),结果是字符串 linux列名、字段路径和目标类型分别回答“从哪一列取、取哪个值、按什么类型计算”。

表结构不必为每次新增属性执行一次变更。但灵活性并不意味着类型消失:数字 120 和字符串 "120" 仍然是不同的值,查询时需要明确预期的类型和异常值处理方式。

实践中,可以把业务主键、事件时间、分区字段等稳定属性放在普通列中,将持续变化的扩展属性放入 Variant。这样,稳定字段仍便于分区和关联,扩展字段则保留演进空间。

二、Variant 的存储与编码

2.1 metadata 与 value:把结构和类型保存在二进制里

未拆列的 Variant,通常由 metadatavalue 两部分共同表达:

组成 保存什么 为什么有用
metadata 编码头、字段名字典及相关偏移 对象可以引用字段名编号,读取时可复用字典解析结果
value 类型标记、标量内容、对象或数组结构及偏移 保留类型,并定位嵌套值的二进制片段

这里的 metadata 是值编码的一部分,与 Catalog 中的表元数据不是同一个概念。对象通过字段名编号、偏移和子值组织内容;数组通过元素偏移定位内容;标量则按自身类型编码。Doris 可以在二进制表示上取字段,无须先还原为 JSON 字符串。

不过,二进制里能够快速定位一个字段,不等于对象存储只需读取这个字段的字节。 如果多个字段仍位于同一个二进制载荷中,实际 I/O 还受 Parquet 页与列块布局影响。要进一步利用列式读取,需要看拆列。

2.2 Shredding:把选定路径变成有类型的物理列

Shredding 可以理解为按路径拆列:例如把 region 拆成字符串列,把 amount 拆成整数列。它们随后可以使用 Parquet 的列式编码、压缩和统计信息。

未拆出的字段,以及与选定物理类型不匹配的值,可以保留在 residual,即残余二进制值中。例如某条记录的 amount 是字符串 "unknown",不能仅因为其他记录的该字段拆成整数列,就丢弃这个值。

沿用前面的 amount 字段,假设文件 writer 选择将它按整数拆列。下面只展示这个对象字段的两类物理槽位,不是完整 Parquet schema:

对象中的 amount 类型子列 typed_value 二进制 fallback:value
数字 120 120
字符串 "unknown" 字符串的 Variant 编码
显式 null Variant null 的编码
字段不存在

表中的“无”表示该物理槽位未存值;Variant null 则由实际存在的二进制编码表达。这个例子假设 payload 本身是对象,讨论的是对象内部的字段。拆列不等于强制类型转换,类型子列为空也不等于逻辑值就是 null。reader 必须结合 fallback 和字段存在性恢复语义。

residual 是残余二进制内容的统称,fallback 强调某条路径不能由类型子列承载时的取值来源。它们可以出现在不同嵌套层级,并非都汇总到根级的一个大对象中。写入时由 writer 按拆列 schema 组织这些物理列,表中对用户暴露的逻辑列仍然是 payload VARIANT

Variant 物理布局.png

图 2|Variant 的两种物理布局:未拆列时保留二进制文档;拆列后,类型子列与残余值共同表达逻辑内容。图中字节仅作编码示意。

同一张表的不同文件,可以拆出不同路径,也可以同时存在拆列与未拆列文件。因此,reader 必须识别每个文件的实际布局,再决定读取哪些列、是否需要残余值,以及何时重建对象。

2.3 与 JSON 的区别:重点在类型、布局与执行方式

Doris 的 JSON 类型采用二进制 JSONB 表示。下面比较的是这种二进制 JSON 表示与湖上 Variant,而不是把 JSON 查询都视为从文本重新解析。

维度 Doris JSONB 本文讨论的湖上 Variant
主要表示 二进制 JSON 文档 带类型的二进制值,可结合拆列布局
类型表达 面向 JSON 语义的二进制表示 可表达 decimal、日期时间、二进制等更多类型
字段访问 在二进制文档上访问路径 二进制路径定位,或直接访问类型子列
子列优化 使用 JSONB 不会自然产生 Parquet Variant 子列 已拆出的路径可以参与列裁剪与统计裁剪
互操作重点 JSONB 的理解或转换 Variant 编码、表格式与各引擎的共同支持

因此,Variant 的价值不只是“把 JSON 换一种编码”。它把类型信息、可选的列式布局和执行引擎的计算能力连接起来。原样交换、低频取值可以使用 JSON;频繁进行路径过滤、聚合和湖上跨引擎读写时,Variant 提供了更多优化空间。

日期时间、二进制等值转成 JSON 文本后,还可能丢失原来的类型身份。文本中转适合受控的 JSON 数据转换,不应默认视为所有 Variant 值的无损交换方式。

三、一条 Variant 查询在 Doris 中怎样执行

3.1 FE 规划:识别类型、路径与扫描任务

用户提交 SQL 后,FE 负责解析、分析与生成执行计划。Catalog 适配层将 IcebergPaimon 中的 Variant 映射为 Doris 能识别的逻辑类型;优化器分析查询需要的列、可识别的嵌套路径和过滤条件,形成扫描任务,再将执行计划分发给 BE。

这里有两类相关但不同的优化:普通列裁剪决定是否需要 payload;嵌套路径裁剪进一步表达只需要 payload['region']payload['amount']。FE 从表达式中收集可识别的常量路径,并区分过滤与投影需求。这些需求传到 reader 后,才能结合实际文件布局转化为物理列读取。路径请求本身并不保证每个文件都能只读叶子列。

02-doris-execution.png

图 3|Doris 的总体执行流程。FE 规划扫描与执行任务,BE 以批次执行算子;查询返回结果,写入则进入目标表 sink 和提交链路。图中的算子为示意,具体次序与分布由执行计划决定。

3.2 BE 执行:扫描、表达式与分布式计算

BE 从湖上扫描数据,组织为包含多列数据的 Block 批次。路径提取、类型转换、过滤、聚合和关联等工作在执行计划安排的算子中完成;需要跨节点交换数据时,由 Exchange 衔接不同执行阶段。过滤可能下推,聚合也可能分布在 Exchange 两侧,不能把概念流程理解为每条查询固定的串行次序。

对 Variant 而言,关键不是让所有算子都理解完整嵌套对象,而是让需要的路径尽早变成可计算的列,同时尽量延后无用的整对象重建。若最终只需要 SUM(amount),大量未参与计算的扩展字段就没有必要反复转换成对象或文本。

四、高效读取:少读无关列,少构造中间对象

4.1 按文件和 Row Group 决定投影范围

以按地区统计金额为例,查询主要需要 regionamount 两条路径。Parquet scanner 会根据当前 Row Group 的物理布局与 residual 情况决定:

  1. 可以证明叶子列足够:读取所需类型子列。

  2. 叶子列还不足以表达结果:保留必要的残余值并参与恢复。

  3. 无法证明局部投影完整:回退到完整 Variant 投影。

这使优化能够适应不同文件的布局,同时保证字段不因裁剪而丢失。动态路径、整对象输出和混合类型都会影响实际读取范围。

variant-three-read-paths.png

图 4|同一条 amount 路径的三种读取方式:直接复用类型叶子、按行组合类型值与 fallback、沿未拆列二进制定位。图中展示可走快速路径的情况;整对象或复杂结构按实际需求恢复。

4.2 拆列路径:复用叶子、补齐残余值,再安全裁剪

情况一:目标路径完全由兼容的类型叶子提供。reader 会沿文件中的物理结构找到目标路径,检查相关层级的有效性与残余值需求。若该路径无需 residual 补充,并且物理类型适合直接使用,就保留已经解码的叶子列,交给后续 CAST 和计算,省去“重新编码为完整对象,再提取同一个字段”的往返。部分需要保留特殊物理语义的类型会转换为精确的 Variant 值,同样可以避免完整对象重建。

情况二:同一路径的不同记录分别由类型列和 fallback 提供值。例如,第一条记录的 amount=120 位于整数子列,第二条记录的 amount="unknown" 位于该字段的二进制 fallback。Doris 会逐行沿路径检查类型值是否存在:类型路径完整时取叶子;在某层需要残余值时,则进入该层的二进制内容,继续定位剩余路径。最终组合的是 amount 这一条路径的结果,保留各行原来的值与类型。

如果目标字段根本没有被拆出,Doris 也可以在对应对象层级的 residual 中查找。字段名到字典编号的解析可以按不同 metadata 复用,只有确实需要解释残余编码时才构建相关索引。因此,“存在 residual”并不意味着“必须重建整个 Variant”。这条优化主要针对能够直接解析的对象键路径;数组或复杂子树不满足快速路径条件时,仍需按实际需求恢复相应值。

这也解释了列裁剪和统计裁剪的区别:前者减少不需要的物理列,后者跳过不匹配的数据范围。当 fallback 中仍有需要参与判断的值时,只看类型子列的统计信息并不足以证明可以跳过。

例如过滤 CAST(payload['amount'] AS BIGINT) > 1000。如果对应类型列在某个 Row Group 的最大值为 900,且转换与残余值条件允许,该 Row Group 就可能被跳过。实现会检查对象键路径、常量比较、类型与排序语义、转换安全性,以及该路径末端是否还存在 fallback 值。Page Index 裁剪也需要相应校验。

4.3 未拆列路径:直接定位与复用解析结果

情况三:文件没有拆出类型子列,目标值保存在 metadata/value 编码中。Doris 会解析 metadata 的字段名字典,建立可复用的索引;访问对象路径时,先把字段名映射为编号,再结合对象中的字段编号与偏移定位子值,沿嵌套层级继续查找,无须先生成 JSON 文本。

同一份字典可以被多行共享使用,路径解析也可以复用已经定位的前缀。例如访问 device.osdevice.version 时,可以减少重复定位 device 的工作。对于已定位的标量,reader 可以组织为适合后续计算的列;对于尚需继续访问的子树,则可以保留路径状态,延后构造完整内容。这些优化减少了重复字典解析、字段查找和中间值构造。

这些优化主要减少字段访问的 CPU 和中间对象开销,与“读取独立 Parquet 子列减少 I/O”属于不同机制。即使文件没有拆列,也无须先把每一行变成 JSON 文本。

五、高效计算:让提取结果尽快进入类型列计算

5.1 同一个 Variant 列,可以保留三种执行形态

文件中的物理布局与内存中的计算表示不是同一件事。为了衔接不同输入和操作,Doris 的 Variant 列支持三种执行形态:

形态 保留的内容 适合的处理
Encoded 编码字节与 metadata 引用 保留二进制值,进行路径定位与序列化
Typed 可空的强类型标量列 对提取出的标量执行类型转换和后续计算
Shredded reader 提供的物理树、路径与子列 直接利用叶子列,按需恢复逻辑值

这些形态是执行层的表示方式,用户看到的逻辑类型仍是 Variant。筛选、截取和行选择可以继续保留拆列形态;reader 能直接提供合适的类型叶子时,就不必先重建完整文档。

04-variant-compute.png

图 5|Variant 计算流程:路径提取连接不同执行形态,类型转换输出标量列,供过滤和聚合使用;整对象请求触发必要物化。图中省略了跨节点传输等序列化边界。

5.2 从路径提取到 CAST,再到向量化算子

看下面这条查询:

SELECT
    CAST(payload['region'] AS STRING) AS region,
    SUM(CAST(payload['amount'] AS BIGINT)) AS total_amount
FROM iceberg_catalog.demo.events
WHERE CAST(payload['amount'] AS BIGINT) >= 100
GROUP BY CAST(payload['region'] AS STRING);

它可以拆成三个容易理解的步骤:

  1. 取路径:从 Variant 中提取 regionamount。如果已有类型叶子,尽量复用;否则沿二进制结构定位相应值。

  2. 定类型CAST 将结果转换为 SQL 所需的 STRINGBIGINT 列。标量转换中存在直接处理 typed 表示的分支,避免绕回完整文档编码。

  3. 按列计算:比较、筛选、分组和求和处理转换后的批次列,接入 Doris 的向量化执行链路。

对于编码形态的标量,转换实现还会按实际类型分组,批量完成转换后恢复原来的行顺序。这让混合类型数据也能衔接批量计算。

5.3 延迟物化:在真正需要时构造值

这里需要区分三个概念:shredding 是写文件时按路径拆列的过程;unshredded 描述未拆列的物理布局;重建或物化 则是在读取与执行时,将需要的子列和残余编码恢复为具体的逻辑值。读取未拆列文件,并不等于先执行一次“反向拆列”。

重建范围取决于请求内容和实际执行路径。只取 amount 且命中路径优化时,可以仅组织该路径的结果;请求 device 子对象,需要得到该子树的完整逻辑内容;请求完整 payload,则必须保留并恢复整个值。复杂子树、数组或未命中快速路径的情况,也可能回退为先重建完整值,再提取所需部分。对于拆列对象,恢复过程按嵌套结构组合类型子字段与未拆出的残余字段,同时保留字段缺失、显式 null 和原始类型的区别,而不是把几段 JSON 文本简单拼接起来。

这种按需恢复要求规划与执行配合:如果下游需要完整对象,扫描就必须保留恢复它所需的信息;只读取了投影叶子的状态不能凭空补回已省略字段。Doris 因此把“直接使用子列”和“生成规范值”的边界显式保留到执行过程中。

跨节点 Exchange 尤其需要注意:reader 持有的内存状态不能原封不动传到另一台 BE。若传输时仍保留拆列表示,序列化会将需要的内容转为可独立传输的表示;这个表示可以只包含规划确认仍需使用的投影,不必总是恢复原始完整对象。如果路径已经转成普通标量列,传输的就是这些列。因此,延迟物化不等于整条分布式查询全程都不物化,也不等于没有数据复制。

对用户来说,最直接的做法是:只选择需要的路径,并明确用于计算的目标类型。若只需地区汇总,就避免把整个 payload 一起放进下游结果;整对象需求可能增加读取、编码和传输工作。

六、高效写回:沿二进制与批次传递数据

本文所分析的实现中,Doris 向 Iceberg 写出未拆列 Variant,目标使用支持 Variant 的表格式版本与 Parquet 数据文件;Paimon 则通过原生 writer 衔接拆列能力。能够读取拆列文件,与能够写出拆列文件,是两项独立能力。因此,Iceberg 读取可以利用已有文件的拆列布局,而当前 Doris 写回链路不会自动生成同样的类型子列。

05-variant-write.png

图 6|Variant 写入流程:Iceberg 通过 Arrow Variant 写出 Parquet;Paimon 通过 Arrow C Data 与 JNI 交给原生 writer。两条路径都在生成文件后进入各自的表提交机制。

6.1 Iceberg:Variant → Arrow → Parquet → 表提交

Iceberg 的 Variant 映射为 Arrow Variant extension,底层结构是 struct<metadata: binary, value: binary>。Doris 的序列化层将二进制值交给相应 builder,再由 Parquet writer 生成文件。

这避免了先输出 JSON 文本、再由另一侧解析并猜测类型的中间过程。

6.2 Paimon:Variant → Arrow 批次 → JNI → 原生 writer

Paimon 写入将 Doris Block 转为 Arrow RecordBatch,经 Arrow C Data 接口送入 Java。Variant 按 Paimon 的 struct<value: binary, metadata: binary> 组织,Java 侧以列式行视图适配 Arrow 数据,并复用行游标交给 writer。

这里利用批次传递减少逐字段跨语言交互,省去为整个批次构造逐单元格 Java 对象矩阵的中间步骤。

Paimon 原生 writer 可以采用显式拆列 schema,也可以推断拆列 schema。Doris 通过该 writer 衔接这些能力;仓库用例覆盖拆列写入和混合文件布局。不同文件拆出不同路径是正常情况,reader 需要按文件解释实际结构。

七、如何使用并验证这些优化

7.1 从正确的值开始

以下示例假设 Catalog 和支持 Variant 的目标表已经准备好,表包含 id BIGINTpayload VARIANT。示例依据分支实现整理,本次未连接集群执行。

INSERT INTO iceberg_catalog.demo.events VALUES
    (1, PARSE_TO_VARIANT('{"region":"west","amount":120}')),
    (2, PARSE_TO_VARIANT('{"region":"east","amount":80,"channel":"app"}'));

INSERT INTO paimon_catalog.demo.events
SELECT id, payload
FROM iceberg_catalog.demo.events;

PARSE_TO_VARIANT 表达的是将 JSON 文本解析为 Variant 内容。CAST('text' AS VARIANT) 表达的是将字符串值转换为 Variant;即使字符串长得像 JSON 对象,也不应把这两种写法混为一谈。

如果来源已经具有 decimal、日期时间等明确类型,应尽量保留类型,不要先把所有值转成字符串再入湖。跨引擎验证时,重点检查数值精度、时间语义、缺失值与 null,以及嵌套数组等边界。

7.2 用 Profile 判断优化发生在哪里

判断 Variant 是否读得高效,可以把整对象查询与单路径查询作为观察入口,但二者输出量不同,不能仅凭端到端耗时宣称加速倍数。应同时看读取字节、算子耗时和物化工作量。

Profile 指标 观察重点
VariantLeafProjectionRowGroupColumns Row Group 中采用叶子投影的情况
VariantResidualProjectionRowGroupColumns 局部投影仍需残余值的情况
VariantFullProjectionRowGroupColumns 完整投影的情况
VariantDirectLeafRows 直接取得叶子值的行数
VariantReconstructedRows / VariantReconstructionTime 完整 Variant 重建的工作量与耗时
VariantUnshreddedDirectSeekRows 未拆列数据按路径直接定位的情况
VariantUnshreddedPrefixReuseRows 嵌套路径前缀复用的情况

7.3 评估性能时,把数据与查询一起看

  • 数据:字段数、嵌套深度、混合类型和缺失值比例。

  • 文件:拆列覆盖了哪些查询路径、残余值比例,以及文件和 Row Group 大小。

  • 查询:单路径还是整对象、过滤选择性、转换目标类型,以及聚合和关联方式。

  • 执行:冷暖缓存、并发度、实际读取字节、跨节点传输,以及 Paimon 的读取路径分布。

Doris 内表的子列与索引属于另一条存储链路,直接查询 IcebergPaimon 文件不会自动获得内表索引。对湖上 Variant,应围绕文件布局、读取路径和实际执行计划解释收益。

八、性能测试

前文介绍了 Variant 的读取与计算机制,下面看它们在湖上查询中的实际表现。测试先由 Spark 将 Variant 数据写入湖表,再分别使用 Doris 和 Spark 执行查询,覆盖拆列(shredded)与未拆列(unshredded)两种布局。

在本轮 Spark 写入数据的 6 组测试中,按相同查询耗时求和,Doris 的冷跑综合加速比为 15.10×,热跑为 19.14×。这里比较的是导入完成后的查询耗时,不包含数据导入耗时。

8.1 测试环境

测试采用某客户场景复刻的 10 个查询 query01–query10,数据存储在 OSS 上,文件格式为 Parquet;湖表包括 Iceberg、Paimon Append 与 Paimon PK。每种表类型分别测试拆列、未拆列两种布局,共 6 个组合;每个引擎在每种冷热状态下均覆盖 60 条次查询。测试环境为 3 台机器,每台 16 核、64 GB 内存;查询引擎为 Doris 4.2 与 Spark 4.0.1。

8.2 整体表现:拆列与未拆列布局均有收益

查询状态 查询条次/引擎 Doris 合计(秒) Spark 合计(秒) 综合加速比
冷跑 60 640.610 9,671.722 15.10×
热跑 60 481.396 9,215.467 19.14×

进一步按布局汇总,拆列数据的冷跑、热跑综合加速比分别为 55.24×、58.64×;未拆列数据分别为 7.06×、9.08×。这些结果说明,在本轮查询与部署配置下,两种布局均表现出 Doris 的查询优势,拆列组合的差距更大。

query-totals.png

图 7|Spark 写入 Variant 数据后的查询总耗时。每根柱汇总一种布局下三种表类型的 30 条查询;冷跑与热跑使用从零开始的相同线性刻度,耗时越低越好。

8.3 分组结果:Paimon 拆列数据的差距最明显

下表每行均为同一组合 query01–query10 的耗时合计,单位为秒。

冷跑

数据布局 表类型 Doris 耗时(秒) Spark 耗时(秒) 加速比
拆列 Iceberg 65.608 1,903.720 29.02×
拆列 Paimon DUP 20.692 2,030.473 98.13×
拆列 Paimon PK 20.617 1,972.225 95.66×
未拆列 Iceberg 186.875 1,299.066 6.95×
未拆列 Paimon DUP 172.981 1,255.808 7.26×
未拆列 Paimon PK 173.837 1,210.430 6.96×

热跑

数据布局 表类型 Doris 耗时(秒) Spark 耗时(秒) 加速比
拆列 Iceberg 61.760 1,893.542 30.66×
拆列 Paimon DUP 17.953 1,920.502 106.97×
拆列 Paimon PK 18.048 1,918.227 106.28×
未拆列 Iceberg 126.834 1,141.699 9.00×
未拆列 Paimon DUP 131.740 1,103.885 8.38×
未拆列 Paimon PK 125.061 1,237.612 9.90×

拆列数据中,Iceberg 的热跑加速比为 30.66×,Paimon DUP 与 Paimon PK 分别为 106.97×、106.28×。未拆列数据的热跑加速比为 8.38×–9.90×。这里的百倍差距来自对应组合的 10 条查询总耗时之比。

query-speedup.png

图 8|六个 Spark 写入组合的查询加速比。每根柱为 Spark 与 Doris 的 10 条查询总耗时之比;两种布局使用相同线性刻度,1× 虚线表示耗时相同。

结语:让灵活结构进入高效的列式计算

Variant 把不断变化的数据保留为带类型的值;二进制编码减少文本转换,拆列布局为列裁剪和统计裁剪创造条件。Doris 再通过路径投影、叶子列复用、标量转换、批次计算与延迟物化,将这些条件转化为实际执行收益。

对社区湖仓用户,最值得抓住的是这条主线:保存时保留类型,读取时按需取路径,计算时尽量利用类型列,写回时保留二进制语义IcebergPaimon 的适配细节有所不同,但都可以沿着这条主线理解和验证。

目前,Variant 以及 IcebergPaimonLance 等多模湖仓能力已合入 Doris 4.2 发版分支,将随 4.2 版本正式发布。如果你正在评估实时湖仓、多模数据分析等场景,可以前往官网了解更多。

posted @ 2026-09-23 18:31  SeleectDB  阅读(39)  评论(0)    收藏  举报