你们调度怎么做的?——回答『用 DataWorks』就结束那就太吃亏了
本文目录
先说结论
调度系统的三大核心能力:依赖管理(DAG)、失败重跑(幂等)、告警分级(不是所有失败都同等重要)。能把这三点讲清楚,调度题就过了。
工具是外壳,机制是内核:DataWorks、Airflow、DolphinScheduler、Azkaban 本质都在做同一件事。讲工具 30 秒,讲机制 2 分钟。
面试最爱问的是异常处理:任务挂了怎么办?上游延迟下游要不要等?重跑会不会脏数据?这几个问题准备好比背工具名更重要。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
『你们调度怎么做的』是继分层之后的第三道必问题。大多数人回答『用 DataWorks』或『用 Airflow』就结束了。这题真正考的是:你懂不懂依赖管理、懂不懂重跑、懂不懂告警。一个只会配 cron 的人和一个懂调度体系的人,在这道题上会被清晰地区分开。
业务场景:我在某短剧出海公司的 DataWorks 调度
我现在在某短剧出海公司用 DataWorks 做调度,每天跑 600+ 任务,分三条主链路:广告归因链路、用户行为链路、内容变现链路。每条链路都有上百个节点,彼此还有跨链路依赖。
# DataWorks 里每个节点的关键配置
# 1. 依赖上游节点(DAG 依赖)
dependencies = [
'ods_event_sync_hour', # 同层上游
'dim_user_daily', # 跨层上游
'ads_prev_day_finish_signal' # 跨链路哨兵
]
# 2. 调度时间(cron 表达式)
schedule = '0 5 * * *' # 每天凌晨 5 点
# 3. 重跑策略
retry = {
'max_retry': 3,
'retry_interval_minutes': 10,
'failure_strategy': 'alert_and_stop' # 挂了就停整条链路
}
# 4. 告警级别
alert_level = 'P1' # P0 电话,P1 群消息+短信,P2 只群消息
关键不是这些配置长什么样,而是为什么这么配。凌晨 5 点是因为上游数据 4:30 到齐、留 30 分钟 buffer;重试 3 次间隔 10 分钟是因为我们 90% 的偶发失败 20 分钟内能恢复;P1 短信是因为这是核心链路挂了投放组上班前就得修好。每一个配置背后都有生产经验。
段落一:依赖管理——DAG 不是配着玩的
调度系统的第一大能力是依赖管理。看起来配依赖很简单,拉几根线就好,实际上依赖配错是生产事故的头号原因。
常见的依赖坑:跨链路依赖没配(上游还没跑完下游就开始跑了,脏读)、依赖配循环了(A 等 B,B 等 A,死锁)、把本该并行的任务串行化(本来 1 小时跑完,拖到 4 小时)。
| 依赖类型 | 例子 | 最容易出的问题 |
|---|---|---|
| 同层依赖 | dwd_order 依赖 ods_order | 必配,一般不出错 |
| 跨层依赖 | dws_gmv 依赖 dwd_order + dim_sku | DIM 维度表容易漏配 |
| 跨链路依赖 | ads_report 依赖上游链路的哨兵 | 忘配最致命 |
| 时间依赖 | 凌晨 5 点跑,但数据 5:30 才到 | 依赖数据不是依赖时间 |
段落二:失败重跑——幂等是命根子
任务失败重跑看起来简单,实际上会把很多人坑死。核心原则一句话:每次任务跑出来的结果必须一样,不能因为跑了两次就多一份数据。这叫幂等。
-- ❌ 非幂等:INSERT INTO(重跑会重复插入)
INSERT INTO dwd_order
SELECT * FROM ods_order WHERE dt = '${bizdate}';
-- ✅ 幂等做法一:INSERT OVERWRITE(覆盖写)
INSERT OVERWRITE TABLE dwd_order PARTITION (dt='${bizdate}')
SELECT * FROM ods_order WHERE dt = '${bizdate}';
-- ✅ 幂等做法二:先删后插(适合没有分区的表)
DELETE FROM dwd_order WHERE dt = '${bizdate}';
INSERT INTO dwd_order
SELECT * FROM ods_order WHERE dt = '${bizdate}';
-- ✅ 幂等做法三:MERGE INTO(变更型表)
MERGE INTO dim_user AS t
USING src_user AS s ON t.uid = s.uid
WHEN MATCHED THEN UPDATE SET t.name = s.name
WHEN NOT MATCHED THEN INSERT (uid, name) VALUES (s.uid, s.name);
我踩过的坑:我在某元宇宙社交应用早期写过一个 INSERT INTO 的任务,一次网络抖动重跑了,结果当天数据翻了一倍。排查了 2 小时,修数据修了 4 小时。从那以后我对所有新任务的 review 第一条就是『这 SQL 幂等吗』。
段落三:告警分级——不是所有失败都要电话吵醒你
很多公司告警不分级,所有任务失败都打电话——结果是告警疲劳,真正重要的告警反而被淹没。好的告警体系一定是分级的。
Plain
P0 级(电话告警,无论几点):
- 核心看板依赖的 ADS 任务失败
- 实时链路断流超过 5 分钟
- 钱相关的任务(财务、广告结算)
P1 级(群消息 + 短信,次日 9 点前处理):
- 常规 ETL 任务失败
- 非核心看板依赖的任务
- 数据质量校验失败
P2 级(只发群消息):
- 临时表任务、探索性任务
- 告警阈值本身不稳定的任务
- 重试 1 次就能自愈的偶发失败
段落四:数据质量校验——调度体系的最后一道门
调度跑完不是结束,还要有数据质量校验(DQC)。DQC 在我这里是调度的标配节点,任何 ADS 任务结束前都要跑 DQC。DQC 挂了和任务挂了是同级告警。
常见的 DQC 校验项:行数波动(今天比昨天少 50% 就告警)、主键唯一(count != count distinct 就告警)、关键字段非空、核心指标环比阈值。这些都是我在实际项目里踩过坑之后加的。
面试高频追问
追问一:上游任务延迟了 2 小时,下游等不等?
看场景。如果是核心链路,必须等,宁可看板晚出也不能出错误数据;如果是探索性任务,可以用『依赖过期降级』策略,跳过延迟上游用前一天的数据代替,但要在看板上打标记。我在某短剧出海公司做跨时区计算时专门设计过一套『降级跑』的机制,上游延迟 4 小时自动降级,同时企微报警。
追问二:任务挂了手工重跑,会不会和下游冲突?
会,所以重跑必须『一键带下游』。DataWorks 和 Airflow 都支持这个功能:选中节点重跑时勾选『带下游』,它会按 DAG 顺序重跑所有下游。不带下游的重跑只适合『下游还没跑』的情况。我们有个 code review 规范:手工重跑核心任务必须两人对齐 + 企微通知,因为带下游跑可能影响看板。
追问三:你们一天跑多少个任务?调度系统性能瓶颈在哪?
我现在这边每天 600+ 任务,DataWorks 本身性能没什么问题,瓶颈主要在计算引擎(MaxCompute 的作业队列)。常见的瓶颈表现:凌晨 3-5 点高峰期作业排队、大任务占住资源导致小任务饿死。优化手段是资源队列隔离(核心链路独立队列)、高峰期错峰调度、大任务拆分。
实战踩坑:跨时区调度踩的大坑
我在某短剧出海公司踩过一个跨时区调度的坑。我们业务覆盖 8 个时区,早期调度全用 UTC+8 的 bizdate 参数,结果美西用户 3 月 1 日的数据被算到 3 月 2 日的分区里——因为 UTC+8 的 3 月 2 日 0:00 对应美西的 3 月 1 日 8:00,用户还在当天。
解决办法:按业务时区切 bizdate。我们在 DWD 层就打标了用户时区,DWS 和 ADS 聚合时按『用户时区的 bizdate』切分。这一改动覆盖了 40+ 个核心任务,修完之后美西的日活数字和业务方对口径了。这个坑我会在第 12 篇 T+1 延迟到 T+2 那里再展开讲。
-- ❌ 错误:统一用 UTC+8 的 bizdate
SELECT DATE(ts) AS dt FROM fact_order;
-- ✅ 正确:按用户时区切 bizdate
SELECT
DATE(CONVERT_TZ(ts, 'UTC', user_tz)) AS dt
FROM fact_order
JOIN dim_user USING (uid);
总结
调度这道题的上限不在『用什么工具』,而在『是否理解三大核心:依赖、重跑、告警』。工具一笔带过,机制讲透。能把『幂等』、『依赖过期降级』、『告警分级』这三个词讲清楚加上一两个踩坑故事,这道题基本就稳了。
下一篇:『请现场写一个连续登录 3 天的 SQL』——面试进入第二阶段 SQL 实操。这道题是数仓最经典的窗口函数面试题,没有之一。
你们公司的调度系统是自研的还是开源的?有没有经历过『调度系统本身挂了』那种最惨的场景?
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长