<span class=“js_title_inner“>Spark做ETL,与Ray/Daft做特征工程的区别在哪里,如何选型?</span>

如果你熟悉 Spark,大概率会有过这样的困惑:

Spark都统治大数据领域十年了,算子成熟、生态完善,为什么做特征工程时,大家还要费劲巴拉去学Ray Data、Daft这些新框架?

更不用说,这些新框架的API,用的还是map、filter、groupby、join,这些熟悉的老伙计。

其实答案很简单:同样的算子,在ETL和特征工程里,使用频率和玩法完全不同。

那么问题来了,Spark与Ray/Daft的区别到底在哪里,我们又要如何对其选型?

01

AI 时代的 Data Infra,从存储中心转向计算中心

很多数据工程师都踩过这样的坑:用Spark做多模态特征工程,明明代码没报错,却越跑越卡:内存爆炸、GPU利用率上不去、模型重复加载。

核心痛点就在于:ETL和特征工程的数据流动逻辑完全相反,而Spark的设计初衷,从一开始就不是为特征工程量身定做的。

sql 复制代码
ETL 典型流程:原始数据 ──► Filter ──► Join ──► GroupBy ──► Sort ──► 数仓表Local     Shuffle   Shuffle    Shuffle特征工程典型流程:原始数据 ──► Download ──► Decode ──► Model Inference ──► 特征表Local       Local      Local (GPU)

ETL(Spark 的舒适区) 是Shuffle密集型任务------数据在节点间大量移动,靠内存缓存加速。

但特征工程是Local 密集型任务,通常数据不动,但需要靠GPU加速模型推理。

这个差异,决定了框架设计的分野。

从更宏观的视角看,这是一次范式转移:AI 时代的 Data Infra,正在从以存储为中心转向以计算为中心。

  • Hadoop时代:一切围绕 HDFS,核心是移动计算比移动数据便宜

  • Spark 时代:内存缓存加速,但核心仍是数据的 Shuffle 与聚合

  • Ray/Daft 时代:一切围绕 GPU,数据流过计算单元,模型权重才是核心资产

理解了这个转移,你就懂了:不是Spark不好,是它的优势,在特征工程场景里反而成了短板。

02

算子的底层逻辑:设计数据怎么流动

在讨论 ETL 和特征工程之前,我们先建立一个分类框架,分类方式,按数据的物理访问模式。

  1. Local(本地):数据不跨分区、无网络I/O,每行数据独立处理(比如给每张图片生成Embedding,各算各的,互不干扰);代表算子:map、filter、project。

  2. Shuffle(交换):数据跨分区重新分布(最耗时),比如Join、GroupBy,需要把相同key的数据搬到同一个节点;代表算子:join、groupBy、sort、distinct。

  3. Broadcast(广播):小数据复制到所有节点,避免重复搬运;代表算子:broadcast join。

  4. Collect(收集):所有数据汇总到Driver,容易OOM,尽量少用;代表算子:collect、take、show。

另外,补充一个关键解读:Local模式的开销是纳秒到微秒级(纯内存计算),而Shuffle模式是毫秒到秒级(要走网络、读磁盘)。

这就意味着:如果你的任务以Local操作为主(比如特征工程),却用了一个优化Shuffle的框架(比如Spark),自然会出现各种效率问题。

除此之外,Local模式内部还能按行变换方式细分(1:1、1:N、N:1),但核心不变:数据原地不动,计算独立完成,这正是特征工程的核心需求。

理解了这些基础分类,我们就可以来分析 ETL 和特征工程的算子使用模式了。

03

深度拆解:ETL为什么会成为Spark主场

下面我们分两部分,结合具体场景和痛点,拆解两者的差异------搞懂这部分,你就知道该怎么选框架了。

3.1 ETL为什么是Shuffle密集型操作

分布式系统中,数据按某种规则分散在多个节点上。当你需要把"相同 key 的数据聚到一起"时(比如 Join、GroupBy),就必须跨节点搬运数据------这个过程叫Shuffle。

makefile 复制代码
Shuffle 前:                        Shuffle 后:节点A: [1,a] [2,b] [1,c]            节点X: [1,a] [1,c] [1,e]  ← key=1 的都在这节点B: [2,d] [1,e] [3,f]  ────►  节点Y: [2,b] [2,d] [2,h]  ← key=2 的都在这节点C: [3,g] [2,h] [3,i]            节点Z: [3,f] [3,g] [3,i]  ← key=3 的都在这

Shuffle 是分布式计算中最昂贵的操作:

  • 网络传输:数据跨节点移动,受带宽限制

  • 磁盘 I/O:中间结果可能落盘

  • 序列化开销:数据需要编码/解码

ETL 之所以是 Shuffle 密集型,正是因为ETL的本质是整理数据------把散乱的原始数据,清洗、关联、聚合后,变成结构化的数仓表,供分析、报表使用,过程中大量需要 Shuffle。

典型ETL任务(都需要大量Shuffle)如下:

  1. 多表关联(Join):订单表关联客户表,相同customer_id的数据可能在不同节点,必须Shuffle后才能关联;

  2. 聚合统计(GroupBy):按日期、类目统计销售额,同一天的订单分布在所有节点,必须Shuffle后才能汇总;

  3. 全局排序(Sort):按金额降序排列,需要先确定分区边界,再Shuffle重新分布数据,最后分区内排序。

4.清洗:过滤脏数据、格式标准化、空值处理

3.2 ETL 的算子使用分布

根据对典型 ETL 任务的分析,其算子使用分布如下:

Shuffle操作占60%(Join、GroupBy、Sort等),Local操作占30%(Filter、Map等),其他操作占10%。

这就是为什么Spark花了大量精力优化Shuffle------比如Exchange 算子的各种优化、Sort-Merge Join vs Hash Join vs Broadcast Join 的选择、Shuffle 文件的管理和清理、Adaptive Query Execution 动态调整分区......本质都是为了适配ETL的核心需求。

04

特征工程为什么是Local 密集型操作

4.1 如何理解特征工程

特征工程的本质是提取信号------把原始数据(图像、文本、音视频)转换成模型能理解的数值信号(Embedding向量、结构化特征),或者提取结构化数据(日志、表格)的结构化特征(数值、类别),供模型训练使用。

因此,特征工程的算子使用分布中,Local操作占80%(Download、Decode、模型推理),Shuffle操作仅占20%(主要是特征聚合),和ETL形成鲜明对比。

以下是几个特征工程的典型场景:

  1. 图像Embedding生成:下载图片→解码→模型推理,每张图片独立处理,不需要跨节点交互;
sql 复制代码
Daft 代码df = (daft.from_glob_path("s3://images/*.jpg").with_column("bytes", col("path").url.download())Local.with_column("image", col("bytes").image.decode())Local.with_column("embedding", embed_image(col("image")))Local + GPU)
  1. 文本Embedding生成:读取文本→批量传入模型→生成向量,每条文本独立推理,不需要与其他文本交互。
makefile 复制代码
Ray Data 代码ds = ray.data.read_parquet("s3://data/texts/")ds = ds.map_batches(SentenceTransformer,compute=ActorPoolStrategy(size=4),num_gpus=1,batch_size=128,)
  1. LLM批量推理:每条prompt独立生成回复,靠GPU批量处理提升效率。
makefile 复制代码
Ray Data LLM 推理config = vLLMEngineProcessorConfig(model_source="meta-llama/Llama-3.1-8B",batch_size=64,concurrency=2,)processor = build_processor(config, preprocess=..., postprocess=...)ds = processor(ds)

4.2 Spark为什么不适配特征工程

Spark的内存缓存设计,是为结构化数据量身定做的。比如一行ETL数据(user_id、name、age、amount),只有50-100字节,百万行也才几十MB,内存缓存能大幅加速Shuffle后的迭代计算。

但特征工程的数据长这样:

apache 复制代码
user_id | image_embedding (512维) | text_embedding (768维) | raw_image1001    | [0.12, 0.34, ...]       | [0.56, 0.78, ...]      | <2MB 图片>

特征工程的数据体积极大,如果一行数据包含2MB以上的图片、512维的Embedding,百万行就是2TB。

而Shuffle会携带整行数据,哪怕只需要user_id做key,Embedding和图片也会被一起搬运,直接占满内存。

这就导致用spark处理特征工程,会出现:

  • 内存爆炸:Spark 的内存本该用于加速计算,现在被不参与计算的大体积数据占满

  • 网络拥塞:Shuffle 带宽被图片、向量等数据吞噬

  • GC压力:JVM 频繁 GC,性能急剧下降

  • GPU利用率极低:Spark的GPU支持是插件扩展的,无法细粒度调度,而且逐条推理无法发挥GPU的批量优势,利用率往往低于10%;

  • 模型重复加载:每个Task都会重复加载模型(比如LLM,动辄几个GB),浪费资源且耗时。

这就是为什么用 Spark 做多模态特征工程会异常痛苦------它的内存模型根本不是为这种数据设计的。

4.3 Ray/Daft的优势是什么?

针对Spark在特征工程中暴露出的各种问题,Ray/Daft做出了众多优化,使其在处理多模态数据时比 Spark 快 4-18 倍。

首先,针对Spark模型重复加载、内存浪费的问题,Ray/Daft采用Actor Pool机制,让模型只加载一次,多个Task复用同一个Actor,彻底避免重复加载带来的资源消耗,大幅节省内存;

针对Spark GPU调度粗放、利用率低下的问题,Ray/Daft支持0.5个GPU的细粒度分配(即两个Actor共享一个GPU),同时可显式控制batch_size,让GPU利用率提升至80%以上;

针对Spark无法感知多模态数据语义的问题,Ray/Daft内置原生多模态类型,能够识别数据语义(比如224x224的RGB图像),进而针对性优化内存布局,避免无效数据搬运。

除此之外,Daft相较于Spark的性能优势,还有三个核心技术支撑:

  1. 无JVM-Python序列化开销:Daft采用Rust+Arrow架构,实现数据零拷贝,而Spark每次调用UDF时,都需要进行JVM与Python之间的序列化操作,耗时严重;

  2. 执行机制差异:Daft采用流式执行模式,数据可直接流过pipeline,无需等待整个stage完成;而Spark采用批量执行模式,必须完成一个stage的所有任务,才能进入下一个stage;

  3. GPU资源管理更高效:Daft通过一个进程统一管理所有GPU资源,避免资源竞争;而Spark多个Executor会同时竞争GPU,导致资源内耗,进一步降低处理效率。

05

ETL与特征工程的核心区别

理解了业务场景的差异,我们就能理解不同框架设计背后的思路。ETL与特征工程,核心区别可以概括为四点

  1. 数据类型:Spark用通用类型(把图像当字节数组,无语义,无法针对性优化);Ray/Daft有AI专用类型(Image、Tensor、Embedding,保留维度信息,可优化内存);

  2. 计算模型:Spark是无状态Task(每个Task都要重新加载模型,重复消耗资源);Ray/Daft是有状态Actor Pool(模型只加载一次,可复用);

  3. 资源分配:Spark是Executor级别(GPU分配给整个Executor,无法细粒度控制);Ray/Daft是Task/Actor级别(可分配0.5个GPU,资源利用率更高);

  4. 优化重点:Spark优化Shuffle(解决网络I/O、数据倾斜);Ray/Daft优化Local+GPU(解决模型加载、批量推理效率)。

06

交集地带:特征聚合

特征工程并非完全不需要 Shuffle。最典型的就是"特征聚合"和"Point-in-Time Join"。

  1. 特征聚合:计算用户过去7天的点击量、平均浏览时长,同一个用户的行为分布在不同分区,必须Shuffle后才能聚合;

  2. Point-in-Time Join:为每条样本关联"当时"的用户特征(避免数据泄露),需要按时间和用户ID Shuffle关联。

这就是为什么现实中往往需要Spark + Ray/Daft 组合使用:

  • Spark 处理 Shuffle 密集的聚合和 Join

  • Ray/Daft 处理 Local + GPU 密集的推理

  • 数据交换:用Parquet格式,实现两个框架的数据无缝衔接。

07

选型建议

选型的思路,其实就一句话,不要试图教会大象跳舞,也不要让猎豹去拉磨,快速指南如下

另外,补充一个典型混合架构(实操可用):

go 复制代码
原始数据↓Spark ETL↓清洗后数据 (Parquet)↓├──────────────────┐↓                  ↓Ray/Daft Embedding    Spark 特征聚合(GPU 推理)            (Shuffle 密集)↓                  ↓└────────┬─────────┘↓特征表 (Parquet)↓模型训练
go 复制代码
作者介绍


陈彪

Zilliz Senior Staff Software Engineer

阅读推荐
教程:如何用飞书+Moltbot,打造你的MilvusAI小助手
RAG优化不抓瞎!Milvus检索可视化,帮你快速定位嵌入、切块、索引哪有问题
Milvus+印度最大电商平台,如何打造服务两亿月活用户的商品比价系统
都有混合检索与智能路由了,谁还在给RAG赛博哭坟?
相关推荐
nvd1132 分钟前
Flink 批处理驱动的湖仓 AI 财务研报流水线:从双流广播汇聚到 Agent 智能决策与原生 Iceberg 落盘全景实战
大数据·人工智能·flink
计算机编程指导师3 小时前
【计算机毕设推荐】基于Hadoop+Spark的公共交通运营数据分析与可视化系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·数据分析·spark·交通
QYR_114 小时前
机械制造用固体润滑涂层市场分析:2032年全球市场规模预计达8.78亿元,CAGR为6.2%
大数据·人工智能·物联网
数字化顾问4 小时前
(144页PPT)数字化转型汽车企业数智化战略规划(附下载方式)
大数据
智慧物业老杨5 小时前
数字底座,重构小区治理秩序
大数据
计算机编程指导师6 小时前
【计算机毕设选题推荐】基于Hadoop的宫颈癌变进程光谱数据分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·数据分析·spark·毕业设计·宫颈癌变
Pniubi6 小时前
分布式锁分类讲解
分布式
guhunyihan7 小时前
无代码系统搭建平台,探索恐象 AI 带来的开发新范式
大数据·人工智能
计算机编程指导师7 小时前
【计算机毕设选题推荐】基于Hadoop+Spark的化妆品销售大数据分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·spark·毕业设计·课程设计·化妆品
benchmark_cc7 小时前
前复权数据每天可能变化,历史数据库应该覆盖还是保留原始价格?
大数据·python·pandas·量化交易·股票数据·quantdash