1. 课程导入与全景认知
当数据平台从“能跑通”走向“每天稳定跑完”,任务性能与成本就会同时成为约束。Databricks 上的大规模任务通常不是单个作业慢,而是成百上千个作业在共享计算资源、共享元数据、共享存储带宽时互相影响:一个倾斜的 Join 可能把某个 Executor 拖成瓶颈,一次小文件过多可能让读取阶段产生大量任务,一段缓存策略不当可能让本可复用的中间结果反复计算。与此同时,DBU 消耗、云实例费用、存储请求费用和人工排障时间会叠加成真实成本。本课程的目标不是教某个孤立参数,而是建立一套“先定位瓶颈,再选择优化手段,最后用指标验证收益”的工作方法。
大规模任务到底难在哪里
在 Databricks 中,一个 Spark 作业从提交到完成,会经历驱动端解析、DAG 切分、Stage 调度、Task 分发、Shuffle 读写、结果落盘等环节。规模一大,问题往往集中在四类:数据倾斜、Shuffle 膨胀、资源错配和小文件与元数据压力。例如,一张 2 TB 的事实表按 user_id 做 groupBy,若少数用户占据 30% 数据量,少数 Task 会处理数 GB 数据,而其余 Task 早已完成,集群出现“长尾”。此时增加 Executor 数量并不能缩短总时长,因为瓶颈是单个 Task 的并行度不足。
另一个常见现象是 Shuffle 写放大。假设原始数据 500 GB,经过一次 repartition(2000) 后,Shuffle Write 达到 1.2 TB,Shuffle Read 达到 1.5 TB。这通常意味着分区键选择不当、空值集中或聚合前未做预过滤。此时应优先检查 spark.sql.shuffle.partitions、AQE 是否开启、是否使用 skew join 提示,而不是盲目扩容。
性能与成本不是两个独立目标
很多人把“优化性能”理解为“加机器”,把“控制成本”理解为“减机器”,两者看似矛盾。但在 Databricks 中,真正有效的优化往往同时改善两者:减少 Shuffle 数据量既缩短运行时间,也减少 DBU 与网络开销;合理使用 Photon 与 Delta 缓存既提升扫描效率,也降低重复计算;把长尾 Task 拆散既提升并行度,也避免为等待少数 Task 而空转大量资源。因此本课程始终用单位处理成本和端到端时长两个指标一起看,而不是只看其中一个。
课程整体学习路径
本课程按“认知—诊断—优化—治理—复盘”推进,但不会把每一节都写成相同流程。前半部分先建立指标体系和诊断方法:如何读 Spark UI、如何看 Query Profile、如何区分 CPU 瓶颈与 IO 瓶颈、如何用 EXPLAIN COST 与 ANALYZE TABLE 判断统计信息是否过期。中段进入具体优化手段:分区与文件布局、Join 策略、AQE 与倾斜处理、Photon 与缓存、集群与作业并发。后段转向成本治理:DBU 归因、标签与预算、作业级成本分摊、闲置资源识别。最后用真实案例把性能与成本放在同一张决策表里做取舍。
学习过程中建议始终带着一个自己的作业来对照。例如你有一个每天运行 40 分钟的 Delta 合并作业,可以记录它的输入数据量、Shuffle 量、DBU 消耗和关键 Stage 时长,然后随着课程推进逐项尝试优化,并保留每次改动前后的指标。这样课程结束时,你得到的不是一堆参数,而是一份属于自己的优化记录。
实战环境准备
为了能复现课程中的观察与验证,建议准备一个 Databricks 工作区,并确保具备以下条件:
- 一个可创建集群的权限,集群模式建议选择 标准或高并发,并开启自动伸缩。
- 一个 Unity Catalog 或 Hive Metastore 中的数据库,用于存放示例表。
- 能够访问 Spark UI 与 Query Profile,便于查看 Stage、Task 和算子级指标。
- 若使用 Photon,确认运行时版本支持,并在对比实验中保持其他变量一致。
下面是一段可用于准备示例数据的代码片段,它会在 tmp_course 库中创建一张带倾斜特征的订单表,后续章节可反复使用:
CREATE DATABASE IF NOT EXISTS tmp_course;
CREATE OR REPLACE TABLE tmp_course.orders AS
SELECT
CASE WHEN id % 100 = 0 THEN 'HOT_USER' ELSE concat('user_', id % 5000) END AS user_id,
id AS order_id,
cast(rand() * 1000 AS decimal(10,2)) AS amount,
date_add('2024-01-01', cast(id % 365 AS int)) AS order_date
FROM range(0, 50000000);
-- 查看分区与文件情况
DESCRIBE DETAIL tmp_course.orders;
这段代码生成约 5000 万行数据,其中 HOT_USER 会占据约 1% 的行数,但足以在按 user_id 聚合时形成明显长尾。你可以先运行一次 groupBy("user_id").count(),观察 Spark UI 中 Task 时长的分布,作为后续倾斜优化的基线。
先建立判断标准,再动手调参
本课程反复强调一个原则:没有基线的优化无法验证。在调整任何参数之前,先记录当前作业的端到端时长、DBU 消耗、Shuffle 读写量、扫描字节数和 Task 长尾比。优化后再用同一组指标对比。若时长下降但单位成本上升,需要判断是否值得;若两者都下降,说明优化方向正确;若两者都未改善,应回到诊断阶段重新定位瓶颈,而不是继续叠加参数。
当你完成本章的环境准备后,就已经具备后续所有实验的基础。接下来将从指标与诊断入手,逐步进入 Databricks 大规模任务优化的具体方法。