Apache Spark 在数据交换中的角色:作为统一计算引擎的 Spark
聊到 Lakehouse 架构,有个东西绕不开——Spark。
说实话,我见过不少团队,数据湖用 Hive,ETL 用 MapReduce,实时用 Flink,机器学习又另起一套。最后搞出四五套计算引擎,运维成本高得吓人。而 Lakehouse 的核心思路之一,就是让 Spark 成为那个「统一计算引擎」。为什么是它?我慢慢讲。
为什么是 Spark,而不是别的?
你想想看,一个数据交换方案,要面对的场景有多杂:
- 批处理:每天几百 TB 的日志清洗
- 流处理:实时接入 Kafka 的点击流
- 交互式查询:分析师临时跑个 SQL
- 机器学习:特征工程 + 模型训练
- 数据湖写入:Parquet / Delta / Iceberg 格式转换
这些活,Spark 一个引擎全包了。我在项目中遇到过一家金融客户,原来用 Tez 跑批、用 Impala 做查询、用 Flink 做实时,三个集群各自维护。后来统一到 Spark 3.x + Delta Lake,集群数量从 3 个减到 1 个,运维人力直接砍半。
核心观点:Spark 的统一性不在于「什么都能做」,而在于「用同一套 API、同一个执行模型,覆盖批、流、查、学四种负载」。
Spark 在数据交换中的三个关键角色
我把 Spark 在数据交换里的作用拆成三层,画了张图帮你理解:
角色一:批处理与 ETL 的核心引擎
数据交换最重的活,就是 ETL。每天几百张表、几十 TB 的数据,从源系统拉到数据湖,再清洗、转换、分发。
Spark 在这块的优势很明显:
- 内存计算:比 MapReduce 快 10-100 倍,尤其适合多阶段 ETL
- 统一 API:一套 DataFrame 代码,既能跑在 10 台机器上,也能跑在 1000 台机器上
- 丰富的连接器:JDBC、Kafka、HDFS、S3、Delta Lake……开箱即用
我的经验:做数据交换时,尽量用 Spark 的 DataFrame API 而不是 RDD。DataFrame 有 Catalyst 优化器,自动做谓词下推、列裁剪、分区剪枝。我曾经把一个 RDD 写的 ETL 改成 DataFrame,同样的逻辑,执行时间从 47 分钟降到 12 分钟。
举个实际例子,从 MySQL 同步一张订单表到 Delta Lake:
// 读取 MySQL 表
val jdbcDF = spark.read
.format("jdbc")
.option("url", "jdbc:mysql://host:3306/db")
.option("dbtable", "orders")
.option("user", "user")
.option("password", "pass")
.load()
// 简单清洗:过滤无效订单
val cleanedDF = jdbcDF
.filter($"status" =!= "deleted")
.withColumn("etl_time", current_timestamp())
// 写入 Delta Lake
cleanedDF.write
.format("delta")
.mode("append")
.save("/data/lake/orders")
这段代码,生产环境每天跑一次,处理 2 亿条订单记录,20 分钟搞定。换成 MapReduce 的话,至少 2 小时。
角色二:流处理与实时数据接入
数据交换不只有批处理。很多场景需要秒级延迟——比如用户行为日志、交易流水、IoT 传感器数据。
Spark 的 Structured Streaming 提供了「批流一体」的能力。什么意思?同一套 DataFrame API,改一行代码就能从批处理变成流处理。
关键点:Structured Streaming 本质上是「微批处理」,把流切成一个个小批次。延迟在 100ms-10s 之间,适合大部分实时数据交换场景。如果你需要毫秒级延迟,那得用 Flink;但如果你能接受秒级,Spark 的生态整合成本更低。
我曾经帮一个电商团队做实时订单同步。他们原来用 Flink 读 Kafka,写到 HBase,再通过 HBase 的 Coprocessor 同步到数据湖。链路长、问题多。后来改成 Spark Structured Streaming,直接从 Kafka 读到 Delta Lake,代码量减少 60%,延迟从 5 秒降到 2 秒。
// 从 Kafka 读取实时订单流
val streamDF = spark
.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "host:9092")
.option("subscribe", "orders_topic")
.load()
// 解析 JSON 并写入 Delta Lake
val parsedDF = streamDF
.selectExpr("CAST(value AS STRING) as json")
.select(from_json($"json", schema).as("data"))
.select("data.*")
parsedDF.writeStream
.format("delta")
.option("checkpointLocation", "/checkpoint/orders")
.table("lakehouse.orders_realtime")
注意:流写入 Delta Lake 时,checkpoint 目录一定要放在可靠的存储上(比如 HDFS 或 S3)。我曾经因为 checkpoint 放在本地盘,节点挂了之后丢数据,排查了一整天才找到原因。
角色三:交互式查询与数据服务
数据交换的最后一公里,是把数据「交出去」。分析师要查、BI 工具要连、下游系统要拉。
Spark 提供了 Spark SQL 和 Thrift JDBC/ODBC 服务,可以直接对外提供 SQL 查询能力。配合 Delta Lake 的优化(比如数据跳过、Z-Order 索引),查询性能可以媲美传统数仓。
| 场景 | 传统方案 | Spark 方案 | 优势 |
|---|---|---|---|
| 临时 SQL 查询 | Presto / Impala | Spark SQL + Thrift Server | 同一引擎,无需额外集群 |
| BI 报表对接 | MySQL / Greenplum | Spark + JDBC 驱动 | 直接查数据湖,无需导入导出 |
| 数据导出 | Sqoop / DataX | Spark 原生写 JDBC / 文件 | 并行度高,支持断点续传 |
举个例子,分析师想查「昨天销售额 TOP 10 的商品」,直接在 Spark SQL 里跑:
SELECT product_id, SUM(amount) as total_sales
FROM lakehouse.orders
WHERE dt = '2024-01-15'
GROUP BY product_id
ORDER BY total_sales DESC
LIMIT 10
这条 SQL 在 Spark 3.x + Delta Lake 上,10 亿条数据量级,秒级返回。嗯,这里要注意——前提是做了分区裁剪和 Z-Order 优化,否则全表扫描还是会慢。
Spark 与表格式的深度集成
Spark 在 Lakehouse 里能成为统一引擎,还有一个关键原因:它对 Delta Lake、Apache Iceberg、Apache Hudi 这些表格式的原生支持。
这些表格式提供了 ACID 事务、时间旅行、Schema 演进等能力,而 Spark 是它们的第一等公民。比如 Delta Lake 的很多特性,就是直接嵌入 Spark 的 Catalyst 优化器里的。
我的建议:如果你从零开始搭 Lakehouse,优先选 Delta Lake + Spark 的组合。Delta Lake 是 Databricks 主导的,和 Spark 的集成最深。Iceberg 也不错,但有些高级特性(比如动态分区覆盖)需要额外配置。
避坑指南:Spark 做数据交换的常见问题
说了这么多好处,也得聊聊坑。我踩过的,你尽量别踩。
- 小文件问题:Spark 写数据默认每个分区一个文件,如果分区数多、数据量小,会生成大量小文件。Delta Lake 的
OPTIMIZE命令可以合并,但最好在写入时控制分区数。 - 数据倾斜:Join 或 GroupBy 时,某个 key 的数据量特别大,会导致单个 task 跑很久。我习惯用
spark.sql.adaptive.skewJoin.enabled=true开启自适应倾斜处理。 - 内存溢出:Spark 是内存计算,但内存不是无限的。遇到 OOM,先检查数据是否倾斜,再考虑调大
spark.executor.memory。别一上来就加内存,先看代码。 - Schema 变更:源表加字段、改类型,Spark 默认会报错。用 Delta Lake 的
mergeSchema选项可以自动演进,但要小心——类型变更可能导致数据丢失。
我曾经踩过的坑:有一次做数据同步,源端把某个字段从 INT 改成了 STRING,我没开 Schema 演进,任务直接挂了。更惨的是,那天是月底结算日……从那以后,我所有生产任务的 Schema 变更都加了告警和人工审批。
小结
Spark 在 Lakehouse 数据交换中的角色,说白了就是「一个引擎,搞定所有」。批处理、流处理、查询分析、机器学习,全用 Spark。配合 Delta Lake 这类表格式,数据交换的链路变得简单、统一、可维护。
当然,Spark 不是万能的。超低延迟场景(毫秒级)还是得用 Flink,复杂机器学习训练还是得用 TensorFlow/PyTorch。但在 90% 的数据交换场景里,Spark 够用、好用、值得投入。