数据延迟3天才到——Late Arriving Data怎么处理才不破坏历史?
本文目录
先说结论
Late Data的5种处理策略,按业务对历史一致性的要求选:丢弃、归入到达日、回写历史日(小窗口)、回写历史日(大窗口+重算下游)、双版本(实时+T+N修正)。每种策略都有明确的适用场景。
最关键的不是技术,而是和业务方达成一致性策略共识:你的报表一旦发出,是「永远不变」还是「会随Late Data修正」?这两种产品定位的实现完全不同。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
「这条数据应该是上周三的,今天才到,怎么处理?」——Late Arriving Data(迟到数据)是数据质量里最隐蔽也最棘手的问题。处理不好,要么破坏历史一致性(已经发出去的报表突然变了),要么丢数据(业务说我的数据呢)。我在出海硬件公司处理过最长7天延迟的渠道回传数据,今天聊聊系统化的处理方案。
Late Data到底什么时候发生?
生产环境我见过的Late Data来源:
• 移动端SDK上报延迟:用户在飞机/地铁上无网络,回到Wifi才上报,可能延迟数小时到数天
• 第三方渠道回传延迟:广告渠道(Meta、TikTok)的转化数据T+1甚至T+7才回传
• 跨境网络延迟:海外业务跨大洲数据传输
• 故障重传:上游系统故障,恢复后批量补传历史数据
• 人工补录:业务方手工修正历史业务数据
策略一:直接丢弃(最简单)
超出Watermark阈值的数据直接丢。Flink默认行为。适合数据量大但单条价值低的场景,比如点击流、曝光日志。
-- Flink SQL:默认行为
SELECT TUMBLE_START(event_time, INTERVAL '1' HOUR), COUNT(*)
FROM kafka_click
GROUP BY TUMBLE(event_time, INTERVAL '1' HOUR);
-- WATERMARK FOR event_time AS event_time - INTERVAL '5' MINUTE
-- 超过5分钟的数据被丢弃
-- 离线侧也可以模拟同样行为
INSERT OVERWRITE TABLE ads_hourly
SELECT DATE_TRUNC('hour', event_time), COUNT(*)
FROM dwd_click WHERE dt = '${bizdate}'
AND DATEDIFF(receive_time, event_time, 'minute') < 5;
适用:行为日志类、单条价值低、量级大、要求实时性。
不适用:订单、支付、库存等业务关键数据,丢一条都会被业务方追责。
策略二:归入到达日(最常见的折中)
Late Data不丢,但记到「到达当天」而不是「事件发生当天」。这样历史数据不变,今日数据稍微「胖」一点。
-- 把所有数据按dt(处理日期)聚合
INSERT OVERWRITE TABLE ads_dau PARTITION(dt='${bizdate}')
SELECT COUNT(DISTINCT user_id) AS dau
FROM dwd_user_login
WHERE dt = '${bizdate}';
-- dt是数据落库当天,event_time是事件发生当天
-- 历史不动,新到的Late Data自然进今天
优点:历史绝对一致,不会出现「昨天报表突然变了」的尴尬。
缺点:当天的数据多了Late Data,看起来「虚高」。需要在报表上加注释说明。
策略三:回写历史日(小窗口修正)
适合「Late Data发生在最近1-3天」的场景。每天回算最近N天的指标,覆盖之前的产出。
-- 每天的ETL不只算今天,还回算最近3天
INSERT OVERWRITE TABLE ads_daily_metric
PARTITION(dt)
SELECT DATE(event_time) AS dt,
COUNT(DISTINCT user_id) AS dau
FROM dwd_user_login
WHERE event_time BETWEEN
CONCAT(DATE_SUB('${bizdate}', 3), ' 00:00:00') AND
CONCAT('${bizdate}', ' 23:59:59')
AND receive_time <= '${bizdate} 23:59:59' -- 只用今日及之前到达的
GROUP BY DATE(event_time);
-- 这样写每天的partition都会被覆盖更新3次
注意点:回写历史日必须配合下游的重算机制。如果下游报表已经把昨天的数据缓存了,今天回写也没用。所以这种策略要求整条链路(DWD/DWS/ADS/BI)都支持「最近N天可变」。
策略四:双版本(实时近似 + T+N最终一致)
最完整但最复杂。实时给「近似值」(标注「数据持续修正中」),T+N(比如T+7)给「最终值」(标注「最终确定」)。这是渠道归因、广告ROI这类场景的标配。
-- 双版本表设计
CREATE TABLE ads_channel_attribution (
dt STRING,
channel STRING,
conv_cnt BIGINT, -- 转化数
conv_amt DECIMAL(18,2), -- 转化金额
version STRING, -- 'realtime' / 'final'
data_freshness_days INT, -- 数据距事件多少天
is_finalized BOOLEAN, -- 是否最终版
update_time TIMESTAMP
);
-- 实时版本:T+1产出,可能继续变
-- 最终版本:T+7产出,不再变化
-- BI根据用户需求展示realtime或final
我在出海硬件公司做广告归因表,转化窗口最长7天,所以ROI数据「7天内不准、7天后冻结」。BI看板上不同时间维度的指标取不同版本——「最近7天」取realtime(带浮动提示),「7天前」取final(冻结)。
五种策略对比
| 策略 | 实时性 | 历史一致 | 成本 | 典型场景 |
|---|---|---|---|---|
| 丢弃 | 高 | 强 | 极低 | 曝光、点击日志 |
| 归入到达日 | 高 | 强(错误归类) | 低 | DAU、行为类粗指标 |
| 回写N天(短) | 中 | 弱(最近N天会变) | 中 | 订单、用户行为细指标 |
| 回写N天(长) | 低 | 弱 | 高 | 退款、库存盘点 |
| 双版本 | 高+低 | 强(T+N后) | 极高 | 广告归因、收入 |
面试高频追问
追问一:业务方说「数据出了不能再变」,怎么办?
和业务方明确两件事:第一,发出后不能变 → 用「归入到达日」策略,Late Data计入今日,历史不变。第二,但希望Late Data有体现 → 加一列「补录数据」单独展示。永远不要让业务方看到一个数字「悄悄变了」,这是数据信任的红线。
追问二:Watermark设多久合适?
看业务可接受延迟的P99。比如95%的数据5分钟内到,99%的数据30分钟内到。Watermark设30分钟覆盖99%,剩下1%走Late Side Output单独处理。设太短丢数多,设太长实时性差。生产实践里常用5分钟到30分钟,超过的极少。
追问三:Late Data处理对Exactly-Once有什么影响?
Late Data处理本质上是「重复计算」——同一个事件可能在Watermark内算过一次,在Side Output里又算一次。所以下游必须有幂等机制(基于event_id的UPSERT),否则会double count。这就是为什么Exactly-Once+Late Data处理是个组合拳,缺一不可。
实战踩坑:海外渠道回传3天,T+1报表发了又改
出海硬件公司初期,Meta和TikTok的转化数据回传延迟可达3天,但产品经理坚持要T+1看到「最终ROI」。我没多想就用「回写历史日」策略,每天回算最近3天。
结果:周一发的「上周ROI=2.5」,周三看变成2.8,周四看变成3.1。运营经理直接炸了——他根据周一的2.5调整了投放预算,结果数据一直在变,决策依据全乱了。
解决方案:我重新和业务方对齐预期,改用双版本策略:
• 实时版本:T+1出,标注「数据持续更新,3天后定稿」
• 最终版本:T+3后冻结,标注「最终数据」
• BI看板上同时展示两个版本,让业务自己选用哪个做决策
教训:Late Data不是技术问题,是「产品定位+用户预期管理」问题。技术能实现各种策略,但选哪种必须先和业务对齐:你的报表是「快但会变」还是「慢但稳定」?这个共识决定一切。
总结
Late Data 5种策略:丢弃、归入到达日、回写短/长窗口、双版本。技术不是难点,难的是和业务对齐一致性策略。永远不要让用户看到数字「悄悄变了」。面试时按策略对比+和业务对齐+一个回写翻车故事讲清楚,能展示完整产品思维。
下一篇聊一个数据科学交叉的话题:上游分布悄悄变了你怎么发现?数据漂移(Data Drift)检测怎么做?
你们最长容忍多久的Late Data?怎么和业务对齐预期的?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长