导读 蚂蚁集团基于 Velox 构建流批一体向量化引擎 Flex 以及其在正确性、易用性以及准确性方面的探索与实践以及未来的规划。
本次分享从以下七方面展开:
1. 向量化技术现状
2. 系统架构
3. 核心功能开发
4. 正确性与数据完整性保障
5. 易用性与开发者体验优化
6. 应用情况与运行成效
7. 未来工作方向与重点
分享嘉宾|刘勇
编辑整理|曾晓辉
内容校对|郭慧敏
出品社区|DataFun
01
向量化技术现状
1. 核心趋势:向量化技术发展现状
不同计算模式的并行度,从上往下并行度越高,无论是单线程还是多线程,使用向量化技术后都有指数级别的性能提升。

向量化的基本原理,本质上是 SIMD 扩展指令集,而 SIMD 能够一条指令同时处理多条数据,相比于传统的单指令,有更高的并行度。
向量化技术发展现状
硬件支持:提供更多寄存器资源SIMD 指令集:CPU 原生支持编译器优化:GCC、LLVM(Gandiva 版本)等编译器已实现自动向量化功能专用库:xsimd 库对于计算密集型应用,向量化技术能显著加速计算效率;但对于软硬件也有相应特殊的要求与配置,如处理器方面需更多的寄存器,软件层面也需要配套。
随着技术的发展,现在绝大多数处理器已经都支持 SIMD 指令集,如 x86 的 SSE 指令集、AVX 指令集、ARM 的 Neon 指令集等,在编译器层面像 LLVM、GCC 编译器亦能够自动化识别代码中可转换成 SIMD 指令的部分,如基于 LLVM 开源计算框架,生成汇编指令集。
现在也有一些开源的专用库:如 xsimd,可同时运行在 ARM 或 x86 平台。

在大数据领域向量化方向做了哪些技术探索。
首先 Photon 是 Databricks 闭源的基于 C++ 全新实现的一个向量化库,Databricks 这些年一直对其内部版本做各种加速,早期的版本每次性能提升可能只有一到两倍,自从上向量化之后,性能提升有 3 到 8 倍左右。
2. 为何选择 Photon 引擎?
在 Apache Spark 框架,以及 Databricks 运行时(DBR)所集成的数千项优化能力的支撑下,Databricks 平台的查询性能多年来实现了稳步提升。而 Photon 引擎—— 一款全新的、完全基于 C++ 开发的原生向量化引擎,在 TPC-DS 1TB 基准测试中,实现了额外 2 倍的性能提升。根据实际业务负载统计,相较于最新版 DBR,客户观测到的平均性能提升幅度可达 3 至 8 倍。自从 Databricks 开源其方案后,大家看见其效果提升,各大主流分布式计算引擎也都往这个方向发力。
如Gluten,它是英特尔开源的基于Spark+Velox的新方案,当前支持ClickHouse;
Arrow-DataFusionComet/Blaze 也是开源项目,是 Spark+DataFusion 的实现方案,DataFusion 是基于 Rust 实现的一个向量化库,类似于 Velox;Gluten-Trino/ Prestissimo,这两个开源项目是基于 Presto+Velox 的实验方案。
主流的分布式计算引擎都在往向量化方向发力,但 Flink 作为流批一体化引擎却缺乏核心的向量化能力。
02
系统架构
1. Flex 框架:架构选型与设计理念
蚂蚁在这个方向做了一些技术探索,由于开源里已经有一些成熟的向量化引擎经过业界的广泛实践与验证,因此在立项之初不考虑重复造车,而是采用类似于 Gluten 的方案,基于 Velox 构建。

也不希望闭门造车,立项之初就跟 Gluten 社区有非常紧密的合作,探讨方案的构建。
Velox 的核心问题是它是面向批处理的计算引擎,它不能处理回撤,而流式计算需要处理回撤,二者语义是冲突的,没法简单的使用 Gluten Substrain Plan 去处理;该方案不仅工作量大,而且相对来说比较复杂,其最终也没法支持状态算子。
刚开始进行流计算处理时,是先支持非状态算子,内部做完 POC 之后,端到端 TPS 性能可提高到 4 倍以上。
2. 引擎选型最终方案
Flex = Flink + Velox验证后内部立项 Flex 项目,选型最终方案采用 Flex = Flink + Velox,Flex 是 Flink 加 Velox 的前缀,全称是 flexible 灵活单词的一个前缀,希望它做能够做到灵活可插。
选型依据
可扩展性:提供一套高可扩展的向量化算子与函数库。性能表现:采用经行业验证的高性能 C++ 类库。社区与生态:具备活跃的开发维护节奏与持续壮大的生态系统。3. 集成策略
集成策略选择的考虑
是否采用 Gluten 项目的 Substrait 执行计划模型?如何实现对有状态算子的支持?集成策略比较与选择
左边是 Flex 的 Original plan,若采用右边 Gluten Substrait Plan 的方式,得插入行转列列转行的算子;同时由于 Velox 不支持状态算子,若采用这种方案,工作量就比较大;尤其在人力少、资源比较紧的情况下,方案不太推荐。

最终采用了 Plan Option2 最后一张图所示的方案,它既支持状态算子,也支持非状态算子,其把算子的批、行转列以及 Velox 的表格计算逻辑全部封装到一个算子里去处理。
4. Flex:一款面向 Flink 的统一向量化引擎
Flex 的架构,跟 Gluten 架构相似,主要分为以下几个模块
JNI 适配层(JNI Glue Layer)原生算子层(Native Operator Layer) 执行计划转换层(Plan Conversion Layer) 数据转换层(Data Conversion Layer) Fallback 回退层(Fallback Layer)统一内存管理层(Unified Memory Management Layer)首先是 JNI 适配层,跟 Velox 交互;
native 原生算子层,负责 Flinkplan 到 nativeplan 的转换;
执行计划转换层:把 Flink 面向行的 RowData 转成 columns 面向列的一个 RowData 数据结构;
Fallback 回退层:负责将 Flink 算子不支持 native 的情况下,回撤处理;
统一内存管理层:对 native 内存进行统一管理。

03
Flex:核心功能开发与实现
围绕 Flex,在功能性、正确性、易用性以及稳定性方面做的一些工作。

1. 性能优化与核心架构
执行计划层优化:提供丰富的优化规则集,提升表达式执行性能。原生算子:支持 SLS、ODPS、Paimon 等原生连接器。高效数据表示:采用基于 Arrow 框架的 ColumnRowData 数据结构,实现高性能的列式处理。在功能性方面,首先在执行计划层进行优化,提供丰富的优化规则集,支持 Calc 算子含有表达式计算的 SIMD 函数会翻译成内部 Calc,以提升表达式计算的执行性能;
第二步是 native 原生算子,当前支持 SLS、ODPS、Paimon;其中 SLS/ODPS,它是蚂蚁内部的一个 Connector,SLS 相当于开源的 Kafka,ODPS 相当于开源里的 Hive,两者都支持以 Arrow 的方式进行数据存储。
第三步是高效数据表示:支持 nativesource,可以把整个数据以 ColumnRow 的方式进行数据流转,避免了额外一层行转列的开销。当前数据流转层是基于 Arrow 框架的 ColumnRowData 数据结构,可实现高性能的列式处理。
功能特性与覆盖范围
丰富的 SIMD 函数库
15+ 个字符串处理函数大量数学运算函数另外对大量的 Flink 内置的函数使用 SIMD 指令集进行重写,支持 15 个以上字符串函数使用 SIMD 指令集进行重写,也支持大量其他数学函数。
2. Projection Reorder 投影重排
如何解决数据转换瓶颈问题?
适用于包含大量引用列(RexInputRef)或长数据值的查询场景?当计算逻辑中包含用户自定义函数(UDF)时,如何实现 Projection Reorder 投影重排支持?

上线过程中也遇到了一些问题,如发现一些 SQL 里面有大量的字段,这些字段数据实际引用的数据内容又很长,参与计算的函数字段又很少,存在函数调用开销比较大的问题,当时是怎么解决的?
针对这个问题的解法不是采用社区 gluten 的方案;如果采用 gluten 方案,通过支持 UDF 用户以 native+Java 的方式,工作量本身比较大,性能提升也非常有限;另一方面是采用 gluten 的方案,fallback 没法用起来;最终采用 Calc 拆成两个算子,使用 SIMD 函数单独拆到 native Calc 里面去做,这样性能能发挥出来。若表达式里含有 UDFs,可通过拆 Calc 算子的方式,把问题完美绕过去。
3. 针对关联算子条件的 SIMD 加速

当时也对 correlation 算子做了加速,correlation 在 query 层面是子查询,内部会 rewrite 去生成 join;若是 UDFs,就不是 rewrite 的方式;在 Flink 层,correlation 本质上是一个 UDFS, UDFS 里含有它的 condition,里面会使用到 SIMD 函数。

4. 针对流 - 流连接条件的 SIMD 加速优化
如例子中使用的 Blink JSON value,把它单独拆出 native calc 做加速;同样的思路也可以对双流交易做加速,若双流交易里 SQL 里含有 on 或 where 条件,可以把它单独拆出 native calc 进行加速。Native 在读写方面反馈的效果很好 Paimon 早期版本 1.0 之前,内部存储格式采用 Parquet,把好 Paimon 数据湖进行加速作为突破口。
这些工作去年已经开始做,当时因为人力少、时间紧,并没有大规模上量。当时没有大规模上线有几大考虑:瓶颈不在表达式计算上,正确性的顾虑以及内部的集群模式尚不支持向量化计算。
5. 执行计划可视化
2025 年针对以上的问题,进行解决突破以实现大规模落地。

当前的实现方案,是将 Flink 的 RowData 流转好 Paimon 的 interval row,然后Paimon 里面 paimon-arrow 模块,有一个 ArrowCSstruct 结构,它引用了 ArrowSchema 和 ArrowArray 的数据和 ArrowArray 的 C++ 代码里是 RecordBatch 的引用,封装一层 JNI,整个链路就打通了。
真正大规模上线会遇到各种各样的问题,比如 Java、C++ 情况下各种 core dump 问题,以及 Paimon 的一些 index,如 Deletion Vector、ColumnIndex 会出现一些行号引用不对导致结果算错的问题,这些问题需要一次次把它解决掉。同时内流算子需要额外的资源,为了避免上线的时候用户使用因资源出现各种稳定性的问题,基于 row 的方式对这些算子自动注入资源。
执行计划可视化:支持原生模式的便捷验证。
为了直观让用户感知到算子是不是 native 执行,基本在前面会加 native 的前缀。

6. 细粒度可配置回退机制
黑名单机制:支持基于函数签名级别的黑名单,实现对向量化的精准管控。智能卸载:仅将由单指令多数据(SIMD)函数构成的表达式转换为原生计算(NativeCalc)。针对 TIMESTAMP、DECIMAL 等复杂 SQL 数据类型,提供自动回退能力。Velox 里的 function 有很多类型,有些类型结果是对的,符合我们的语义,有些是不对的。支持细粒度的回退机制,以用户能够配置的方式,对函数的某些类型做细粒度的回退。Fallback 仅含有 int 函数才会翻译,考虑到正确性的问题,加上蚂蚁金融业务的特殊性,像这种容易出错的类型当前还是简单的进行,需要支持一些细粒度的回退机制。
7. 可配置函数映射
支持 SQL 函数的自定义映射。在 Flex 中提供原生实现,并配备优先级覆盖机制。比如 Flink 或 Velox,有些函数可能不一样,但是语义是一样的,就没必要重写,可通过配置执行 map 的方式处理。如果 Flex 有大量代码函数结果不正确,在 Flex 里每次编译修改开发的时间可能要几个小时;这些若在 Flex 的 CPP 模块里实现引用、原生实现,并配备优先级覆盖机制,整个编译时间可从几个小时降低到几分钟。
04
Flex:正确性与数据完整性保障
正确性问题;由于 Flink 和 Velox 是两套引擎,如何保证大量函数语义行为是对齐的?
没法保证,需要一种自动化的手段去提前发现二者语义行为上的差异。
那么为此我们开发了两套验证体系,一个是函数级别的,一个是作业级别的。
1. 自动化函数级验证
无缝复用现有 Flink 单元测试用例在旧版技术栈与新版技术栈上同步执行测试逐比特自动比对测试结果,精准发现任何差异
针对自动化函数级验证,由于 Flink 里面有大量的单元测试代码,可无缝复用里面的单元测试逻辑与用例。
因为 Flink 的历史原因,旧版技术栈与新版技术栈上共存,它的内置函数的实现方式有两套,new stack,legacy stack,为此实现两套测试框架,在旧版技术栈与新版技术栈上同步执行测试;每天定时跑,可逐比特自动比对测试结果,可提前把一些行为语义不正确的问题修掉。
如支付宝里的访问、点击、曝光等作业,用 Flink 原生 Java 去跑,经常会出现跑不动、追不动、跑不稳以及作业 owner 频繁应急的一些问题;通过 C++ 的 Velox 应用现在已经把这些问题完美解决掉。
关键发现与影响
定位出 Flink 原生 Java 函数中存在的 4 处正确性问题发现 Flink(Java 实现)与 Velox(C++ 实现)的函数行为之间存在多处语义不一致问题通过这套工具,发现大量的 Velox 不对齐的问题,同时也发现若干个 Flink 本身引擎边界非常难找的一些问题通过这套工具找到处理掉。
2. 作业级验证
仅仅函数行为语义正确了,还是没法保证这些函数嵌在一起的时候,使用各种类型的结果能够保证正确,所以还得实现一套面向作业级别的一个自动化比对框架;25 年上半年做了 Minos 系统,它能够在作业里一键在 SQL 改写生成两个作业,把结果写到 Hive 里面,以 5 分钟为单位的分区写到 Paimon 表分区里;然后作业检查分区情况进行全量比对。
3. 端到端自动化对比框架 Minos
针对复杂数据集,验证完整 SQL 查询语句的执行正确性。
图中所示是核对系统的一个基本样例,在运行中的核对系统的作业情况;以 5 分钟为单位进行核对,右边是核对的报告,以及核对报告的详细信息,如字段分区的结果对不对,准确率情况等。
05
Flex 框架:提升易用性与开发者体验
Flex 框架:在易用性以及开发者体验方面做了大量工作:
提前发现哪些 Flink 函数 Velox 是不支持的,避免上线向用户推广的时候发现大量的不支持,那对用户的体感、体验是非常差的;为此开发了一套自动编译的工具,将线上的所有 SQL 拿过来重新跑,自动化跑一遍,提前把不支持的一些函数支持掉。

自动化兼容性分析器
静态分析作业执行计划,在迁移前即可判断作业是否支持原生运行,大幅缩短接入周期。已发现大量暂不支持的函数。基于 JMH 的端到端性能验证框架
验证向量化优化的实际收益,并精准定位性能回退问题。向量化监控面板
为所有向量化作业提供实时洞察与核心指标监控。
实现 SQL 函数的效果怎样?有没有性能回退?
开发了一套基于界面器框架的单端性能测试框架,也搭建了大盘,了解作业的运行情况以及性能情况,从 JMH 框架的效果数据可看到字符串函数里有查找语义的使用 SIMD 指令集的实现效果是非常好的;如 position、location、instr 这些性能可提高十倍以上。
06
应用情况与运行成效

最终的运行效果,已大规模上量
已支持 6800+ 个作业已在 Flink 1.18.1.2 版本中默认启用 节省 3 万 + 个 CPU 核心 Paimon 端到端(TPS)提升 50%目前蚂蚁最大流量的曝光、访问、点击已全面应用, 并在蚂蚁的广告投放、碰一碰、闪购、灵光平台、资金调度、大规模安全防护这些项目中应用起来。
07
未来工作方向与重点

一是支持全链路向量化。当前对算子的支持是有限的,主要支持非状态算子,如果一个 native source 和 native cache 算子中间插入一个 Watermark 算子;若 Watermark 算子不支持向量化,要么 fallback 回撤,要么就要行转列开销,这样性能就比较差;所以下一步是实现全链路向量化。
二是实现高效、低开销的行数据到行向量的转换。当前的方案采用 Arow 做数据流转,整个链路比较简单,但整个链路的数据拷贝开销比较大,期望能够将数据转换层直接从 Flink RowData 转成 Vector 的数据结构。
三是支持更多的状态算子,Flink 的核心是状态算子,当然状态算子的瓶颈不是在向量化工作;支持、效果究竟怎样其实也不好说,
四是能支持更多的体系架构,如 ARM、GPU 架构。
五是能够支持全类型 SQL 数据类型,包括复杂的行类型、数组类型与映射类型。
六是支持更多 SIMD 函数,包括数学运算类函数。
以上就是本次分享的内容,谢谢大家。