写点什么

【跨国数仓迁移最佳实践 2】MaxCompute SQL 执行引擎对复杂类型处理全面重构,保障客户从 BigQuery 平滑迁移

  • 2025-07-21
    陕西
  • 本文字数:3463 字

    阅读完需:约 11 分钟

本系列文章将围绕东南亚头部科技集团的真实迁移历程展开,逐步拆解 BigQuery 迁移至 MaxCompute 过程中的关键挑战与技术创新。本篇为第二篇,跨国数仓迁移背后 MaxCompute 的统一存储格式创新。


注:客户背景为东南亚头部科技集团,文中用 GoTerra 表示。

业务背景和痛点

随着大数据与 AI 时代的快速发展,全球数据总量已经突破了百 ZB 量级,其中半结构化(Json 等)和嵌套数据结构(PB 等)占比超 50%,传统技术处理此类数据,面临存储成本高,计算效率低等问题日益凸显,亟需高效的性能优化技术。当前主流大数据处理引擎框架普通采用复杂数据类型(struct、map、array)来灵活处理半结构化等数据,相关功能支持和处理性能成为现代数据架构技术竞争焦点。


Google BigQuery 和阿里云 MaxCompute 作为全球领先的商业化数据处理平台,均历经十余年产品和技术迭代。其中 MaxCompute 日均处理 EB 级数据和数千万作业,服务覆盖了全球多行业客户,因此也一直在持续丰富复杂类型的功能完备和性能优化。在‌GOTO 项目从 BigQuery 迁移至 MaxCompute‌的过程中,‌复杂数据类型的处理能力对标‌成为关键任务,需确保功能和性能‌持平甚至超越‌BigQuery,以实现作业平滑迁移与资源成本优化。

技术现状

简单介绍三种典型复杂数据类型特征和使用场景:


  • Array 类型:一组同类型元素的集合(如 array),支持下标索引访问元素,广泛应用于存储和处理列表形式的数据,例如商品列表,多值属性等

  • Map 类型:键值对集合(如 map<bigint, string>),支持通过键直接访问值,典型场景包括动态属性、键值配置等

  • Struct 类型: 包含多个字段的复合类型(如 struct<id:bigint, value:string>),每个字段具有独立名称和类型,支持通过字段名访问元素,常用于用户画像标签、订单信息等


MaxCompute 支持复杂类型的现有能力和瓶颈:


  • 存储层:支持复杂类型列式存储(Aliorc 文件格式),显著降低存储成本,具体用法参考官网介绍

  • 计算层: 支持行式复杂类型数据处理。尽管功能完备,但性能仍有较大优化空间。


由此可见,一条复杂类型数据处理,需经历列式读取,换转成行式计算,再转换成列式写入,多次转换开销巨大,并且大部分算子行式处理数据性能也较差,导致部分场景性能相比 BigQuery 存在一定的差距。


为彻底解决优化这些痛点,MaxCompute SQL 执行引擎对复杂类型处理进行了全面重构,整体支持复杂类型列式的内存存储结构,对各个算子进行深度适配优化,整体处理性能实现质的飞跃,部分场景提升超 10 倍,基本追平 Bigquery 对复杂类型的计算处理性能,且在某些场景实现性能超越,最终保障了 GOTO 项目海量作业的平滑迁移,同时大幅节省计算资源

技术方案简述

这里主要介绍两个核心优化重构,一个是复杂数据类型的计算优化重构,另一个是 Unnest with subquery 的框架优化重构,这两项优化在复杂类型场景普遍都带来 1-10 倍以上的性能提升。

列式复杂数据类型的计算优化重构

优化分成两个阶段:


  1. 各算子基于当前行式复杂类型结构持续优化计算逻辑,减少不必要的拷贝和计算。

  2. 将行式结果彻底改造成列式结构,并适配各算子列式计算模式。

行式复杂类型浅拷贝优化

现有算子接收到行式复杂类型的输入数据,进行计算后输出,需全量数据深拷贝,对于数据量大的复杂类型,开销极高。因此数据结构和处理过程进行深入优化,支持绝大部份处理流程进行浅拷贝优化,只要数据不发生变更,只需拷贝数据引用即可,不涉及数据本身的拷贝,优化覆盖表达式/聚合/Join/Window 等主要算子,部分场景提升高达 100 倍+,效果显著

复杂类型列式内存结构和计算优化重构

虽然行式优化取得了不少进展,但部分痛点依然存在,比如计算过程效率低下,无法高效适配向量化计算;计算过程中子元素列裁剪无法生效;每行数据的内存结构复杂,且需存储重复的辅助结构,严重制约整体计算性能。


为了显著提升性能,彻底改造复杂类型内存存储和计算框架,由行式转为列式内存结构,推动各个算子采用高效的列式计算思想适配优化,整体性能得到质的飞跃,部分场景提速超 10 倍。


列式内存结构重构

如上图所示,重构之后,每行复杂类型数据不再单独存储,而且采用类 arrow 结构,对于 Batch(多行数据集合)中某一列复杂类型,相同位置的子元素连续内存存储,因此每个 Batch 仅需存储一份辅助结构(如列名等),有效节省内存空间,而且子元素数据连续存储,内存访问效率也显著提升。

算子适配列式复杂类型计算

列式结果改造完成后,各 SQL 算子需进一步适配优化,由于行式和列式计算方式差异显著,此部分优化近乎完全重构,并针对不同场景进行了精细化适配。


列式复杂类型计算模式可分为以下三类:


  • 数据透传模式: 数据从源表列式读取后,中间处理无变更,全程零拷贝传输,最后直接列式写入目标表,性能优化到极致。常见于数据搬迁,简单列裁剪数据清洗等场景。

  • 数据追加模式: 算子一次性处理复杂类型数据后顺序输出到列式结构中,不会再次修改,可有效利用内存缓存优化和向量化处理提升整体性能。常见于表达式/Window/Join 等主要算子。

  • 数据修改模式: 算子需多次随机修改复杂类型数据,此模式不太适合列式内存结构,其存储的多行数据内存是连续的,如果随机修改中间某一行数据,可能破坏后继其他行数据的内存结构,因此此场景需退化成行式处理。常见于聚合函数处理等少数算子。

Unnest with subquery 的框架优化重构

BigQuery 的 Unnest(array)操作极为常见,输入参数通常是一个 Array 复杂类型,负责把 Array 中每一个子元素展开为一个单独行进行输出,GOTO 项目大量任务使用此操作,迁移到 MaxCompute 之后,由于缺乏原生支持,需自动转换成等效的 Lateral View + Explode 操作来执行。但当 Unnest 嵌套于 SQL 子查询等较为复杂用法时,MaxCompute 处理会变得极其复杂,性能急剧下降。


下面举个例子:


create table src(a bigint, b array<struct<c:bigint, d:string>>);
select (select max(c) from unnest(b)), a+100, (select collest_list(d) from unnest(b) where d='test')from src;
复制代码


上述 SQL 示例中,MaxCompute 执行时会转换成如下 Plan 来执行(示意图):



可见此 Plan 主要存在如下问题:


  • 同一源表多次读取

  • 相同 unnest 操作重复执行

  • 需多次复杂 Join 拼接数据


因此如需大幅提升性能,需在语法解析,Plan 构建,算子计算等各流程对此场景完全重构优化。

SQL Plan 优化

为了优化上述三个问题,重构优化后的 Plan 如下图所示(示意图),


  1. 仅读取一次源表,按需把读取数据列推送至各 Subquery 计算

  2. 定义新的 CorrelatedJoin 操作,负责把每个 Subquery 定义成一个单独的 internal subplan,然后按行对齐和拼接每个 Subquery 的计算输出列,整行输出,消除计算量很重的多次 Join 操作

  3. 定义新的 internal subplan 结构,本质是一棵 OperatorTree, 并做针对性的 Plan 优化改造,如消除 shuffle 操作

  4. 对于多个 subquery 中相同 Unnest 操作,支持子树合并,消除冗余的 unnest 操作(在优化中)。


经过上述优化后,基本可解决之前的性能痛点,贴近性能最佳的计算 Plan,后续还需要 SQL 执行层进行计算适配。


SQL 执行层优化

主要负责执行物理 Plan,并发处理数据,输出结果,优化重构主要分以下几步:


  1. 适配优化后的新物理 Plan,解析为可执行算子

  2. 实现新 CorrelatedJoin 算子,负责驱动 internal subplan 执行,并且把各 subplan 执行的结果按行对齐和高效拼接,然后输出整行结果到后续算子执行

  3. 实现新的 internal subplan 数据处理框架,需支持按照每行复杂类型进行一次性处理和输出的语义,跟通用算子执行框架差异显著。

上线效果和业务价值

提升效果案例

列式复杂类型重构优化性能提升案例


上图示例展示的是用户某个作业的其中一个 Stage M6 的处理过程,TableScan1 从源表中读取了多层嵌套的复杂类型数据,经过了 TableScan / Project / TableFunction / Shuffle 等算子处理,基于行式复杂类型结构进行处理,M6 Stage 整体耗时超过 5 分钟,切换到优化后的列式复杂类型处理,整体 Stage 耗时缩短到 31 秒,**速度提升了 10 倍+**,效果极为显著。

Unnest with subquery 重构优化性能提升案例


上图示例展示了用户某个作业经过 Unnest With subquery 重构优化后,整体作业性能提升了 80%+,由于整体 sql 比较复杂,这个优化只涉及到其中部分 stage,**其中命中优化的算子提速 3 倍+**,效果极为显著。

业务价值

GOTO 整体项目,包含复杂类型的表数量超 4 万张,日均处理复杂类型的 SQL 作业超 20 万+,通过上述优化重构之后,大部分 SQL 性能提升 20%~10 倍+,日均减少 2000+ Cpu Core 消耗,极大的助力了 GOTO 项目按时保质的从 Bigquery 平滑迁移到 MaxCompute,实现降本增效的目标。


后续会持续完善打磨列式复杂类型在各个场景的数据处理优化,并全面推广到 MaxCompute 平台所有业务场景中,预计全域上百万作业将显著受益,大幅节省业务计算成本,技术普惠到所有用户。


用户头像

还未添加个人签名 2020-10-15 加入

分享阿里云计算平台的大数据和AI方向的技术创新和趋势、实战案例、经验总结。

评论

发布
暂无评论
【跨国数仓迁移最佳实践2】MaxCompute SQL执行引擎对复杂类型处理全面重构,保障客户从BigQuery平滑迁移_人工智能_阿里云大数据AI技术_InfoQ写作社区