面试官问'你们ETL调度怎么做的'——调度不是配个Cron这么简单
先说结论
调度的核心不是'定时执行',而是'依赖管理+失败处理+SLA保障'。定时执行只是最基础的功能,面试官看重的是你怎么管理作业之间的上下游关系、怎么处理失败情况、怎么保证核心报表按时出数。
调度系统的选择和你的团队规模强相关:10个作业用Cron就够了,50个作业需要Airflow/DataWorks,200个以上的作业需要完善的调度平台+监控+告警。面试时说清楚你的规模和对应的方案选择,展示你的工程判断力。
最容易出问题的是作业失败后的处理策略:是直接重试?重试几次?下游要不要等?要不要告警?这些策略设计得好不好,直接决定了你凌晨会不会被电话叫起来。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
ETL调度看起来不起眼,但面试官一问就能看出你的工程化水平。很多人讲调度就是'我们用了Airflow/DataWorks,定时跑'——这等于什么都没说。面试官想听的是:你的作业之间有什么依赖关系?怎么处理失败重试?怎么保证SLA?怎么应对作业堆积?这些才是真正体现你做过大规模ETL管理的地方。我在三家公司用过不同的调度方案,今天把调度的核心问题和实战经验讲清楚。
调度系统的四个核心能力
| 核心能力 | 含义 | 没有会怎样 | 面试要讲的要点 |
|---|---|---|---|
| 依赖管理 | 作业B必须在作业A完成后才能执行 | 下游跑了但上游还没跑完,数据不对 | 你怎么定义和管理DAG依赖关系 |
| 失败处理 | 作业失败后的重试、告警、阻断策略 | 一个作业失败了但没人知道,下游用了脏数据 | 你的重试策略和告警机制 |
| SLA保障 | 核心报表在约定时间前必须出数 | 早上9点报表没出,业务方炸了 | 你怎么监控和保障SLA |
| 资源调度 | 多个作业同时跑时的资源分配 | 作业抢资源导致全部变慢 | 你怎么控制并发和资源优先级 |
我在出海硬件品牌的调度架构
我在某知名出海硬件品牌管理100+个Spark作业的调度。从EMR迁移到Databricks后,调度工具从Airflow换成了Databricks Workflow。整个调度架构是这样的:
// 调度架构总览
// 执行窗口:每天 02:00 - 03:30(UTC+8)
// 总作业数:100+个Spark SQL作业
// 调度工具:Databricks Workflow
// DAG结构:三层依赖
// 第1层(02:00):ODS数据加载
// ├─ ods_device_activation (从S3加载原始设备激活数据)
// ├─ ods_user_events (从S3加载原始用户事件数据)
// ├─ ods_orders (从S3加载原始订单数据)
// └─ ods_ad_performance (从API拉取广告平台数据)
// 第2层(~02:25):DWD清洗(依赖第1层全部完成)
// ├─ dwd_device_activation (依赖 ods_device_activation)
// ├─ dwd_user_events (依赖 ods_user_events)
// └─ dwd_orders (依赖 ods_orders)
// 第3层(~03:00):DWS聚合(依赖第2层对应的DWD)
// ├─ dws_activation_daily (依赖 dwd_device_activation)
// ├─ dws_user_daily (依赖 dwd_user_events)
// └─ dws_order_daily (依赖 dwd_orders)
// 03:30 SLA点:所有DWS表必须就绪,Tableau自动刷新报表
依赖管理:DAG设计的实战原则
依赖管理是调度的核心。设计得好,作业并行度高、执行效率高;设计得差,一个作业卡住整个链路。
原则一:最小依赖原则。一个作业只依赖它真正需要的上游,不多不少。比如dws_activation_daily只依赖dwd_device_activation,不需要等dwd_orders完成——它们之间没有数据关系。这样dwd_orders即使延迟了,也不影响激活报表。
原则二:宽依赖要拆分。如果一个DWS表同时依赖5个DWD表,而其中4个都很快只有1个很慢,整个DWS就会被那个慢的拖住。这时候要考虑把DWS拆成两部分——不依赖慢表的部分先出数,依赖慢表的部分单独跑。
-- 宽依赖拆分示例
-- ❌ 拆分前:dws_dashboard同时依赖5个DWD表
-- 任何一个DWD延迟都会导致整个看板延迟
-- dws_dashboard: 依赖 dwd_a, dwd_b, dwd_c, dwd_d, dwd_e
-- ✅ 拆分后:按依赖关系拆成两个子表
-- dws_dashboard_core: 依赖 dwd_a, dwd_b(核心指标,先出数)
-- dws_dashboard_ext: 依赖 dwd_c, dwd_d, dwd_e(扩展指标,后出数)
-- Tableau报表配置两个数据源:
-- 核心看板 → dws_dashboard_core(03:00就绪)
-- 详细分析 → dws_dashboard_ext(03:30就绪)
-- 效果:即使扩展数据延迟,核心看板仍然按时出数
失败处理:重试策略和告警机制
作业失败是常事——网络抖动、资源争抢、上游数据延迟都可能导致失败。关键是怎么自动化地处理失败。
| 失败类型 | 常见原因 | 重试策略 | 告警级别 |
|---|---|---|---|
| 瞬时失败 | 网络抖动、临时资源不足 | 自动重试3次,间隔5分钟递增 | 第3次失败后告警 |
| 数据未就绪 | 上游数据还没到 | 等待+轮询,每10分钟检查一次,最多等1小时 | 等待超时后告警 |
| 逻辑错误 | SQL bug、Schema变更 | 不重试,直接告警 | 立即告警+阻断下游 |
| 资源不足 | 集群负载高、OOM | 扩容后重试1次 | 告警+申请临时资源 |
// 我的失败处理策略设计
// 重试配置
// max_retries: 3
// retry_delay: [5min, 10min, 20min] -- 指数退避
// retry_on: ['TIMEOUT', 'NETWORK_ERROR', 'RESOURCE_ERROR']
// no_retry_on: ['SQL_ERROR', 'SCHEMA_ERROR'] -- 逻辑错误不重试
// 告警升级策略
// 第1次失败:自动重试,不告警
// 第2次失败:发钉钉消息到值班群
// 第3次失败:电话通知值班人员 + 阻断所有下游作业
// 为什么要阻断下游?
// 因为上游数据没准备好就跑下游,下游会用到旧数据或空数据
// 产出的报表数字是错的,比没有报表更危险
SLA保障:怎么让报表按时出数
SLA(Service Level Agreement)是数据团队和业务方的约定:核心报表必须在什么时间前出数。我在某出海硬件品牌的SLA是每天早上9点(北京时间)。
保障SLA的关键是:倒推时间、预留buffer、关键路径优化。
// SLA倒推法
// SLA时间:09:00 报表可用
// Tableau刷新:08:30 开始刷新,约20分钟
// ↑ 所以DWS/ADS必须在 08:30 之前就绪
// ETL执行窗口反推:
// DWS聚合:~25分钟(~03:00完成)
// DWD清洗:~30分钟(~02:35完成)
// ODS加载:~20分钟(~02:20完成)
// 启动时间:02:00
// Buffer:预留了5.5小时(03:30 → 08:30)
// 这5.5小时用来应对:
// - 作业重试(3次重试约1小时)
// - 手动修复(如果自动重试解决不了)
// - 集群扩容等待
// 实际运行情况:
// 正常情况 03:30 就全部跑完,离SLA有5.5小时余量
// 迁移到Databricks后ETL时间减少40%,余量更充足
面试高频追问
追问一:如果某个上游数据延迟了3小时,你怎么处理?
分两步处理。第一步:评估影响范围。查看这个上游数据有哪些下游依赖,影响哪些报表。如果只影响非核心报表,可以等;如果影响核心报表,要启动B计划。第二步:B计划。我们对核心报表设计了降级方案——如果今天的数据没到,用昨天的数据先填充,在报表上标注'数据截至昨日'。这样业务方至少有参考数据可用。等上游数据到了后,自动回填正确数据。这种降级方案要提前设计好,不能等出事了才想。
追问二:作业越来越多,调度越来越慢,怎么优化?
三个优化方向:第一,并行度优化——检查DAG依赖关系,把不必要的串行改成并行。很多时候作业之间其实没有数据依赖,只是被配置成了串行执行。第二,资源池隔离——核心作业和非核心作业用不同的资源池,避免非核心作业抢占核心作业的资源。第三,淘汰无用作业——定期Review所有作业,停掉下游没人用的作业。我在某出海硬件品牌做过一次清理,停掉了20多个没人用的作业,释放了大约15%的集群资源。
追问三:你们有没有数据回填(Backfill)机制?
有。数据回填是指重新计算历史某个时间段的数据。触发场景通常是:发现历史数据有bug需要修复、新增指标需要补算历史数据、或者上游数据延迟到了需要补数。我们的回填机制是:调度系统支持传入自定义日期参数,指定要回填的日期范围。回填时会跳过正常的依赖等待,直接读取指定日期的上游数据。回填完成后自动触发下游重算。需要注意的是回填要错峰——不能在正常ETL窗口内跑回填,否则会抢资源。
实战踩坑
我在某知名出海硬件品牌遇到过一次调度事故。有一天凌晨,ODS层的数据加载作业因为S3的API限流失败了。自动重试3次后仍然失败,告警发到了钉钉群。但那天是周六,值班人员没及时看到。
更要命的是,DWD层的作业设置的依赖是'定时触发'而不是'上游完成触发'——也就是说ODS没跑成功,DWD照样在02:25开始跑了。DWD读到了昨天的ODS数据,产出了重复的数据。下游DWS也跟着跑了,最终报表的数据全是昨天的重复数据。
// 事故根因:依赖类型配错了
// ❌ 错误配置:DWD用定时触发
// schedule: cron(25 2 * * *) -- 每天02:25固定执行
// 问题:不管上游成功没成功都会执行
// ✅ 正确配置:DWD用上游完成触发
// depends_on: [ods_device_activation: SUCCESS]
// 只有上游成功完成后才触发执行
// 修复措施:
// 1. 把所有DWD/DWS作业改成上游完成触发
// 2. 周末告警升级为电话通知(不只是钉钉消息)
// 3. 加了'空跑检测':如果上游分区没有新数据就终止
教训:依赖管理是调度的生命线。'定时触发'和'上游完成触发'看起来差别不大,但出问题时差别巨大。凡是有上下游关系的作业,必须用依赖触发而不是定时触发。另外,周末和节假日的告警策略要专门设计——钉钉消息在这些时候基本等于没有告警。
总结
调度的核心是依赖管理、失败处理和SLA保障。依赖管理要最小化、支持并行;失败处理要分类型、有梯度(重试→告警→阻断);SLA保障要倒推时间、预留buffer。面试时讲清楚你管理了多少作业、DAG结构是什么样、怎么保证按时出数,就很有说服力。最关键的是讲你遇到过什么调度事故以及怎么改进的——这展示了你的实战经验深度。
下一篇聊一个容易被忽视但面试加分的话题:面试官问'你们怎么做成本控制的'——大数据不是烧钱比赛
你们的ETL调度用的什么工具?遇到过什么调度事故?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长