数仓任务延迟了,你怎么排查?数据新鲜度 SLA 怎么定?
考察意图
考察候选人会不会把「数出不来」从一句抱怨落成可操作的新鲜度契约:先定义该何时就绪、用什么水位衡量,再沿依赖链定位卡住的那一层。只说「看看调度是不是挂了」却说不清 SLA、水位和对下游承诺,这题就浅了。
回答框架
- 先定义新鲜度:业务要在什么时刻用到哪张表的哪个分区,超时算违约。
- 把 SLA 写成可监控指标:目标完成时间、允许延迟、水位字段,而不是「尽快跑完」。
- 排查沿依赖链:调度没起、上游没到、自己算慢、写出失败、下游误判。
- 延迟处置:告警升级、降级口径、补数窗口;根治靠依赖治理和容量,不靠人肉重跑。
- 主动补水位:事件时间 / 处理时间 / 分区 dt 三套时钟不要混用。
参考答案(口述版)
一句话:新鲜度是「这张表的这个分区,必须在几点前对下游可读」;任务延迟先沿依赖链找卡住的那一层,再决定告警、降级还是补数,而不是先重跑一遍碰运气。
先把契约钉死。 面试里「数据晚了」没有统一含义。要先问:晚的是 ODS 落库、DWD 明细,还是 ADS 看板?业务要的是「T+1 早上 9 点能看昨天」,还是「T+0 每小时刷新」。把这句话写成 SLA:
- 对象:哪张表、哪个分区(通常是 dt=昨天)
- 就绪时刻:例如每天 08:30 前该分区对只读账号可见
- 允许延迟:08:30–09:00 黄灯,09:00 后红灯并升级
- 衡量字段:分区 _ready 标记、或 max(event_time) / max(load_time) 水位,禁止只看「调度显示成功」
调度成功 ≠ 数据新鲜。作业空跑、写出 0 行、分区目录在但 _SUCCESS 没有,对下游都是没就绪。
排查按依赖链,不要先翻自己的 SQL。 从业务看板往上游走,每一层只问三件事:该分区到了没有、到的水位够不够、作业有没有失败重试耗尽。
1. 调度没起:定时器、日历、上游 sensor 一直等;Airflow/DolphinScheduler 里看 next_run、依赖是否 all_success。
2. 上游没到:Kafka 积压、业务库抽数锁表、ODS 分区空。自己任务再快也造不出数。
3. 自己算慢:数据膨胀、倾斜、小文件、Shuffle 打满。看 Stage 耗时,对一下昨天同时段。
4. 写出失败:权限、磁盘、并发写冲突;任务标红但部分文件已落,重跑必须覆盖分区。
5. 下游误判:表已就绪,看板缓存、权限、取错分区(取到 dt=今天 的空分区)。
口述级水位检查(离线日分区):
-- 业务承诺:dt=昨天 的 dws 必须在 08:30 前可读
-- 水位用业务事件时间,不要用入库 wall clock 冒充新鲜
SELECT
dt,
COUNT(*) AS rows,
MAX(event_time) AS event_watermark,
MAX(load_time) AS load_watermark
FROM dws_order_daily
WHERE dt = DATE_SUB(CURRENT_DATE(), 1)
GROUP BY 1;
-- rows=0 或 event_watermark 远小于昨天 23:59 → 未就绪,即使作业显示 success
延迟了怎么对外。 先分级:核心收入/监管表走电话+事故单;普通分析表看板标「延迟中」并给出预计时间。能降级就降级:小时表暂时用昨天闭环,实时看板回落到 T+1。禁止在不完整分区上对外报 GMV。补数窗口写进 SLA:09:00 前自动重跑两次,超过则人工介入并冻结当日对比。
根治。 依赖只接「分区就绪」而不是「上游任务绿了」;热点表做分区就绪标志或数据质量门禁。容量按峰值留余量,不要把 SLA 订在历史最快完成时间上。跨团队的表,SLA 写进接口契约:提供方保证就绪时刻,消费方禁止在那之前启动。
收尾:面试官听三句话——契约(何时何表何分区)、排查(沿依赖链五层)、处置(告警/降级/补数)。能把「任务绿了」和「数据新鲜」拆开,这题就过了。
关键词
面试官可能的追问
追问 1:SLA 订 8 点,任务经常 8 点 10 分才完,是把 SLA 改松还是把任务改快?
先看承诺对象。若业务开会就在 8 点要用,改松 SLA 等于把事故合法化,应拆作业、预聚合、扩容,把 P95 完成时间压进承诺。若 8 点只是历史习惯、真正消费在 9 点半,把 SLA 改成 09:00 并加黄灯,比天天红灯更好。口诀:SLA 对齐真实消费时刻,用 P95/P99 而不是平均完成时间;平均 7:50、尾巴 9:30,平均好看但契约已经破了。
追问 2:上游是别的团队的表,他们没有就绪标志,你的任务怎么依赖?
不要只依赖他们调度任务名。最低限度:sensor 检查分区目录存在且行数/水位过门槛,或约定一张 _ready(table_name, dt, ready_at)。短期可「延迟 30 分钟再跑」当缓冲,但这是拍脑袋,高峰日会一起炸。长期要签接口:提供方保证 dt 分区在 T 点前可读,变更 schema 或推迟要提前通知。跨团队没有就绪契约,freshness SLA 就是空的。
追问 3:流式链路的新鲜度怎么量?和离线分区 dt 有什么不同?
流式看的是事件时间水位:当前窗口关窗所需的 event_time 已经到齐到哪。处理时间(作业 now())只能说明集群在跑,不能说明数据新。例如 Watermark=10:05 而墙上已经 10:20,新鲜度就是「事件时间落后 15 分钟」。离线日分区的 SLA 是点时刻(08:30 分区可见);流式 SLA 是滞后上限(P99 延迟 < 5 分钟)。两套时钟禁止混报:不要拿 Flink checkpoint 成功去向业务保证「订单已经到 10 点」。
常见踩坑
- 把调度成功当成数据就绪,空分区或 0 行也对外报数
- SLA 写成「尽快」,没有对象表、分区和截止时刻,无法监控也无法追责
- 延迟后直接重跑自己的任务,没查上游水位,空跑浪费高峰资源
- 用入库 load_time 当新鲜度,源库卡住时墙上时间很新、事件时间已经停了
记忆锚点
新鲜度是「何时何表何分区可读」;任务绿了不等于数据新,沿依赖链查水位再决定重跑还是降级。