Flink 向量化执行引擎:架构设计与实现
摘要:Flink向量化引擎通过三层架构实现流批一体场景的性能突破。在Flink内核层引入Plugin机制实现算子替换,胶水层完成RexNode到SubstraitIR的转换,Native层基于定制DuckDB实现SIMD加速。创新点包括行转列、流转微批、ZeroCopy传输等技术,使简单SQL性能提升7倍以上,复杂SQL提升3倍,成为首个完整支持流式向量化的Flink实现。项目通过非侵入式改造和标
一、背景与目标
随着实时计算场景对性能要求的不断提升,传统基于行式处理的 Flink 引擎在复杂 SQL 场景下面临性能瓶颈。为满足用户“降本增效”的核心诉求,我们于 2025 年启动 Flink 向量化引擎 自研项目,目标是通过向量化执行技术大幅提升流批一体场景下的端到端性能。
项目成果显著:
- 简单 SQL 性能提升 ≥7 倍
- 复杂 SQL(含 Join/Window 等)性能提升 ≥3 倍
二、整体架构设计
Flink 向量化引擎采用 三层架构:
┌───────────────────────┐
│ 用户 SQL (Flink) │
└──────────┬────────────┘
▼
┌───────────────────────┐
│ Flink 内核层改造 │ ← 行转列 / 微批 / Plugin 机制
└──────────┬────────────┘
▼
┌───────────────────────┐
│ 胶水层(Java) │ ← RexNode → Substrait Plan
└──────────┬────────────┘
▼
┌───────────────────────┐
│ Native 执行层(C++) │ ← Substrait → DuckDB 计划 + SIMD + Zero Copy
└───────────────────────┘
核心创新点:
- 行转列(Row-to-Columnar):在算子边界将 RowData 转换为列式内存格式,为向量化执行奠定基础。
- 流转微批(Stream-to-MicroBatch):在保证低延迟前提下,聚合小批次数据以提升 SIMD 利用率。
- SIMD 指令加速:利用 CPU 向量指令并行处理列数据。
- Zero Copy 数据传输:减少 Java 与 Native 层间的数据拷贝开销。
- Substrait 标准计划中间表示:实现 Flink 与 Native 执行器的解耦。
三、Flink 内核层改造
为支持向量化执行,我们在 Flink 源码中引入 Plugin 机制,实现非侵入式扩展。
1. 新增 StreamExecPlugin 接口(SPI)
public interface StreamExecPlugin {
void updateGraph(StreamGraph graph);
}
通过 Java SPI 机制加载,允许外部模块在 StreamGraph 构建完成后介入,替换算子实现。
2. 关键类修改
表格
| 类名 | 修改内容 |
|---|---|
PlannerModule |
在解析阶段通过 ServiceLoader 加载 StreamExecPlugin 实例 |
StreamExecutionEnvironment |
在 executeAsync() 中调用 plugin.updateGraph(graph);新增 setExecContext(Object context) 方法用于传递 RelNode |
CommonExecCalc 等算子 |
调用 setExecContext(relnode),将逻辑计划节点传递给胶水层 |
StreamTask |
根据是否存在 Plugin 动态构建 OperatorChain,替换为 Native 算子 |
2.1 基于 SPI 的插件化扩展机制
通过PlanerModule类结合ServiceLoader实现StreamExecPlugin接口实现类的加载,打造胶水层与 Flink 内核的解耦胶水层,基于 Java SPI 机制实现插件的动态加载,无需修改 Flink 原生核心代码,提升架构扩展性。
2.2 定义 StreamExecPlugin 核心扩展接口
自定义实现StreamExecPlugin接口,核心包含updateGraph(StreamGraph graph)方法,该方法为胶水层的核心入口:胶水层实现此接口,遍历 StreamGraph 中的所有算子,将算子的原生 Java 实现替换为Native 实现,完成 StreamGraph 的向量化增强。
2.3 StreamTask 与 OperateChain 适配
改造StreamTask类,增加向量化插件判断逻辑:根据是否加载了StreamExecPlugin插件,动态更新OperateChain的构建逻辑,使 Task 执行时调用 Native 算子实现,而非 Flink 原生 Java 算子。
2.4 StreamExecutionEnvironment 扩展
对 Flink 核心执行环境类StreamExecutionEnvironment做两处关键扩展:
- 在
executeAsync方法中加载向量化插件,并调用插件的updateGraph方法完成 StreamGraph 的增强; - 添加
setExecContent(Object obj)方法,支持将算子的RelNode(关系表达式节点)传递至胶水层,为后续计划转换提供核心元数据。
2.5 CommonExecCalc 算子改造
改造CommonExecCalc类(Flink 核心计算算子基类),在算子初始化阶段调用setExecContent方法,将当前算子的RexNode(核心表达式节点,包含 Project、Filter、Join 等计算逻辑)传递至胶水层,实现算子计算逻辑的透传。
2.6 StreamGraph 层核心增强
在 StreamGraph 层完成Native Plan 转换,为每个需要向量化执行的算子(Project、Filter、Join、Window、Source、Sink 等)添加 Native 标识,并将算子的 RexNode、RelNode 等核心信息透传至胶水层,作为后续生成 Native 执行计划的依据。
3. 执行流程集成
Flink 原有 Job 提交流程:
Program API → StreamGraph → JobGraph → ExecutionGraph → Physical Execution
向量化扩展后,在 StreamGraph 阶段 插入转换逻辑:
- 遍历所有算子(Project/Filter/Join/Window 等)
- 提取其
RexNode表达式树 - 通过 Plugin 将算子标记为“可向量化”
- 替换 Operator 实现为 Native Wrapper
四、胶水层设计(Java + C++)
4.1 Java 胶水层
职责:
- 接收 Flink 传递的
RexNode(如RexInputRef,RexCall,RexLiteral) - 将其转换为 Substrait IR Plan
- 管理 Native 执行上下文生命周期
- 实现数据格式转换:
RowData→ 列式存储格式(Arrow-like)
支持算子:
- Source / Sink
- Project / Filter
- Hash Join / Window Agg
- UDF(需注册映射)
具体功能如下:
- 自研 Substrait 转 DuckDB 执行计划:DuckDB 本身不支持 Substrait 计划解析,此功能为完全自研,实现标准化 Substrait 计划到 DuckDB 原生执行计划的转换,是向量化执行的核心自研点;
- 多数据源扩展:完成自研数据源适配,支持 Nexmark 基准数据源、Pulsar 消息队列数据源、Hudi 湖仓数据源的抽取与列式数据加载;
- UDF 扩展:开发 DuckDB 原生不支持的业务自定义函数,实现 UDF 的向量化执行,适配业务多样化的计算需求;
- Native 向量化计算执行:基于 DuckDB 引擎执行向量化计算,充分利用 SIMD、zero copy 等技术,最大化发挥硬件算力;
- 执行结果回传:将 Native 计算结果按需转换为列式 / 行式数据,回传至 Flink 内核或直接输出至 Sink。
4.2 C++ Native 执行层
职责:
- 解析 Substrait Plan
- 自研 Substrait → DuckDB 执行计划转换器(DuckDB 官方不支持 Substrait)
- 扩展 DuckDB:
- 支持 Pulsar、Hudi 数据源读取
- 实现缺失的 UDF(如时间窗口函数、复杂类型处理)
- 集成 SIMD 优化内核(如 AVX2/AVX-512)
- 支持 Zero Copy 内存管理(通过 JNI DirectBuffer)
注:DuckDB 本身为嵌入式分析引擎,我们对其进行了深度定制,使其支持流式微批执行模型。
具体功能如下:
- RexNode 精细化解析:针对 Flink 算子的 RexNode 不同类型(
RexInputRef、RexCall、RexLiteral)做差异化解析,提取算子的计算逻辑、输入引用、常量值等核心信息; - Substrait 计划生成:将解析后的 RexNode 转换为标准化 Substrait 执行计划,Substrait 作为跨引擎的执行计划中间表示,实现 Flink 与 DuckDB 的计划解耦;
- 全算子 Native 实现开发:完成 Source、Sink、Project、Filter、Join、Window 等所有核心算子的 Native 胶水层实现,确保算子逻辑可被 Native 引擎识别与执行;
- 数据行转列转换:将 Flink 原生的行式
RowData转换为列式存储格式数据(Column Storage Format Data),为 Native 向量化计算提供数据基础; - 跨层通信适配:实现与 C++ 胶水层的高效通信,完成 Substrait 计划、列式数据的传递。
五、核心优化技术融合
Flink 向量化引擎的性能提升,基于多项底层优化技术的深度融合,贯穿从数据输入到计算执行的全流程,具体如下:
- 行转列(Row to Column):将行式存储的 RowData 转换为列式存储,使相同数据类型的字段连续存储,为 SIMD 硬件加速提供基础,提升内存访问效率与计算并行度;
- 流转微批(Stream to Micro-Batch):将流式的连续数据转换为微批处理,增大计算批处理量,减少调度开销,提升向量化计算的效率;
- SIMD 技术:单指令多数据,利用 CPU 的 SIMD 指令集,对列式数据进行批量并行计算,大幅提升计算效率;
- Zero Copy(零拷贝):在数据流转过程中,减少内存拷贝次数(如直接内存映射、堆外内存使用),降低 CPU 与内存开销,提升数据传输效率;
- Native 编译执行:基于 C++ 实现核心计算逻辑,避免 Java 虚拟机的性能开销(如 GC、解释执行),充分利用硬件底层算力。
更多推荐


所有评论(0)