数仓分层ODS/DWD/DWS的调度怎么编排——分层调度设计
先说结论
分层调度的核心原则:每层独立DAG+层间依赖+分层SLA。ODS一个DAG(凌晨1:00定时触发)、DWD一个DAG(依赖ODS完成)、DWS一个DAG(依赖DWD完成)、ADS按业务域拆多个DAG(依赖DWS)。每层有独立的SLA——ODS最晚1:30、DWD最晚2:30、DWS最晚3:30、ADS最晚4:30。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
数仓分层(ODS/DWD/DWS/ADS)不只是建模问题也是调度问题——每一层的任务都要在正确的时间以正确的顺序执行。在某短视频平台,100+个ETL任务按分层编排成3个DAG,每层的调度策略不同:ODS层重数据就绪、DWD层重数据质量、DWS层重时效性、ADS层重可用性。分层调度设计是大数据面试的综合题——考调度+数仓+工程三方面能力。
分层DAG设计
| 层 | DAG | 触发方式 | SLA | 任务数 | 重点 |
|---|---|---|---|---|---|
| ODS | dag_ods_daily | 定时1:00+Sensor | 1:30 | 20 | 数据就绪 |
| DWD | dag_dwd_daily | 依赖ODS完成 | 2:30 | 40 | 数据质量 |
| DWS | dag_dws_daily | 依赖DWD完成 | 3:30 | 30 | 计算效率 |
| ADS | dag_ads_*(按业务域) | 依赖DWS完成 | 4:30 | 20+ | 产出可用 |
# 分层DAG的依赖编排
# DAG 1: ODS层
with DAG('dag_ods_daily', schedule='0 1 * * *') as ods_dag:
sensors = [S3KeySensor(task_id=f'wait_{t}', ...) for t in ods_tables]
loaders = [SparkSubmitOperator(task_id=f'ods_{t}', ...) for t in ods_tables]
for s, l in zip(sensors, loaders): s >> l
# 每张表:先等数据到达再加载
# DAG 2: DWD层
with DAG('dag_dwd_daily', schedule=None) as dwd_dag: # 不定时,依赖触发
wait_ods = ExternalTaskSensor(
task_id='wait_ods_complete',
external_dag_id='dag_ods_daily',
external_task_id='ods_final_check',
mode='reschedule', timeout=3600)
dwd_tasks = [SparkSubmitOperator(task_id=f'dwd_{t}', ...) for t in dwd_tables]
checks = [PythonOperator(task_id=f'check_{t}', ...) for t in dwd_tables]
wait_ods >> dwd_tasks >> checks
每层的调度策略差异
ODS层——重数据就绪:核心关注'数据到了没有'。每个数据源用Sensor等待,到了就加载。Sensor设超时+soft_fail——数据源延迟不阻塞其他数据源。ODS加载必须是INSERT OVERWRITE保证幂等。
DWD层——重数据质量:核心关注'数据对不对'。每个DWD表加载后跑数据质量检查(null率、行数、值域)。检查不通过告警但不阻塞下游——DWS用的是DWD的上一个成功版本。
DWS层——重时效性:核心关注'快不快'。DWS通常是最重的计算(多表JOIN+聚合),是关键路径的主要组成。优化DWS的Spark作业比优化其他层收益更大。DWS之间的依赖要精确——dws_revenue只依赖它实际用到的dwd表不要依赖所有dwd。
ADS层——重可用性:核心关注'下游能不能用'。ADS产出后推送到BI系统/API缓存/导出文件。ADS任务失败需要立即告警——因为直接影响终端用户看到的数据。
实战经验:某短视频平台的分层调度
100+任务按分层编排:ODS 20任务(并行加载20张源表)、DWD 40任务(清洗+质量检查)、DWS 30任务(聚合)、ADS 15任务(报表产出+推送)。
时间线:1:00 ODS开始 → 1:20 ODS完成 → 1:25 DWD开始 → 2:30 DWD完成 → 2:35 DWS开始 → 3:30 DWS完成 → 3:35 ADS开始 → 4:00 ADS完成。总计3小时,SLA 4:30有30分钟缓冲。
关键设计:DWD和DWS之间不是等所有DWD完成才开始DWS——而是每个DWS表只等它依赖的DWD表完成就开始。这样DWS不用等最慢的DWD表,整体时间缩短了约30分钟。
面试高频追问
追问一:为什么不把所有层放在一个DAG里?
三个原因:① 一个DAG太大难以维护(100+Task的DAG图看不清);② 不同层的触发方式不同(ODS定时、DWD依赖触发);③ 不同层的故障域要隔离——ODS层的问题不应该直接导致ADS层的DAG状态异常。分层DAG让每层可以独立重跑、独立监控、独立排查。
追问二:DWD质量检查不通过怎么办?
不阻塞下游但发告警。DWS会使用DWD的最新成功分区(可能是今天的也可能是昨天的)。告警后值班人排查DWD问题——修复后手动重跑DWD当天分区,DWS在下一次调度或手动触发后更新。这个策略的核心是'宁可用旧数据也不让报表空白'。
追问三:DataWorks的分层调度和Airflow有什么区别?
DataWorks的节点依赖是在Web UI上拖线设置,比Airflow的代码定义更直观。DataWorks还支持'基线'概念——给每层设基线时间,超过基线自动告警并追踪延迟原因。相当于内置了SLA监控。Airflow需要自己配sla_miss_callback实现类似功能。
总结
分层调度讲三层:① 架构设计(每层独立DAG+层间依赖+分层SLA,不要一个巨大DAG);② 每层策略差异(ODS重就绪、DWD重质量、DWS重时效、ADS重可用);③ 精确依赖(DWS只等它用到的DWD不要等所有DWD,缩短关键路径)。能讲出100+任务的分层时间线和精确依赖缩短30分钟的优化,面试官觉得你对数仓调度有系统设计能力。
下一篇聊任务间数据传递:XCom传了个10GB DataFrame怎么办?
你们数仓调度按层编排吗?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长