|
@@ -354,47 +354,104 @@
|
|
|
- dim 表数据量增长导致 sche shuffle 性能下降,评估改"分区裁剪 + 局部重算"
|
|
- dim 表数据量增长导致 sche shuffle 性能下降,评估改"分区裁剪 + 局部重算"
|
|
|
- 业务库消除"主动置 NULL" 场景后,可放宽到字段级 COALESCE 简化写法
|
|
- 业务库消除"主动置 NULL" 场景后,可放宽到字段级 COALESCE 简化写法
|
|
|
|
|
|
|
|
-### ADR-09 DWD 事件表跑批回算窗口 N=2
|
|
|
|
|
|
|
+### ADR-09 DWD 事件表回算窗口 N
|
|
|
|
|
|
|
|
-- **状态**:已采纳
|
|
|
|
|
|
|
+- **状态**:草案(待 PG 实测 delta_days 分布定 N)
|
|
|
|
|
+
|
|
|
|
|
+ **修订历史**:
|
|
|
|
|
+
|
|
|
|
|
+ - 2026-05-10:初版采纳 N=2(commit `a9b6eaa`),基于"业务库 update_time 严格同步刷新"通用假设
|
|
|
|
|
+ - 2026-05-27:dwd.dt=20260522 验数缺 31%(51,684 vs PG 74,925)暴露 N=2 在订单状态机长生命周期场景下不够。合并原 ADR-11 重评估到本 ADR,方向"扩大 N"待 PG 实测分布定
|
|
|
|
|
+
|
|
|
|
|
+- **背景**:
|
|
|
|
|
+
|
|
|
|
|
+ 本项目 ods 层按 ADR-03 严格按 `update_time` 归位 dt(双源 union 完整捕获 `update_time ∈ [T-1, T)` 范围所有版本)。dwd 事件表按业务时间(如 `payment_success_time`)分区。
|
|
|
|
|
+
|
|
|
|
|
+ 业务库存在**两类漂移**:
|
|
|
|
|
|
|
|
- 本项目 ods 层按 ADR-03 严格按 `update_time` 归位 dt(双源 union 完整捕获 `update_time ∈ [T-1, T)` 范围所有版本),dwd 事件表理论上单分区扫 `ods.dt=T-1` 即可拿到所有"业务时间 T-1 + update_time T-1"的事件。
|
|
|
|
|
|
|
+ **1. 跨零点 OLTP 漂移**(短期,≤ 1 天)
|
|
|
|
|
|
|
|
- 但业务库 OLTP 写入业务事件时间(如 `payment_success_time`)与 `update_time` 不一定严格同步刷新——典型场景:跨零点支付,`payment_success_time = T-1 23:59:59.500`,业务库 ORM 延迟 600ms 刷 `update_time = T 00:00:00.100`,该行落 `ods.dt=T`;事件本应归 `dwd.dt=T-1`,但 dwd 单分区扫 `ods.dt=T-1` 拿不到。这种漂移在业务高峰跨零点场景下不可忽略。
|
|
|
|
|
|
|
+ 业务事件时间与 `update_time` 不严格同步刷新。典型场景:`payment_success_time = T-1 23:59:59.500`,ORM 延迟 600ms 刷 `update_time = T 00:00:00.100`,该行落 `ods.dt=T`,事件本应归 `dwd.dt=T-1`,但 dwd 单分区扫 `ods.dt=T-1` 拿不到。N=2 兜底足够 cover。
|
|
|
|
|
+
|
|
|
|
|
+ **2. 业务状态机漂移**(长期,5~14 天)
|
|
|
|
|
+
|
|
|
|
|
+ 订单从支付(status=101)→ 发货(103)→ 收货(104)→ 退款(301),每次状态变更刷 `update_time`。一个 5-22 支付的订单可能 5-26 才有最后一次 update。在补数场景下(DataX 用 `update_time` 锚点拉),PG 不保留历史快照 → 只能拉到当前最新版本归位到漂走的 `ods.dt`。
|
|
|
|
|
+
|
|
|
|
|
+ 2026-05-27 `dwd.dt=20260522` 验数缺 31% 即此问题:raw_ods 补数 5-26 拉 [5-21, 5-23) 窗,5-22 订单 `update_time` 已漂到 5-25~5-26 → ods 归位漂走 → dwd N=2 看不到。
|
|
|
|
|
+
|
|
|
|
|
+- **决策**:
|
|
|
|
|
|
|
|
- 本项目第一张 dwd 事件表 `dwd_trd_order_pay_apd_d` 落地反复讨论后定下范式(5/9 一度采纳"信 OLTP 契约不回算" → 5/10 自破契约编漂移场景 → 复盘 sessions 5/9 T8-T10 发现违反共识 → 用户重新拍"后端不可信"→ 业界范围 N=2/3 拍 N=2 → commit `a9b6eaa`)。
|
|
|
|
|
|
|
+ - dwd 事件表 sche 回算近 N 日:扫 `ods.dt IN [${dt}-N+1, ${dt}]` + 过滤 `DATE(business_event_time) IN (${dt}, ${pdt})`
|
|
|
|
|
+ - 写入 `dwd.dt IN (${dt}, ${pdt})`:`INSERT OVERWRITE PARTITION (dt)` 动态分区(kb/26 §8 默认 DYNAMIC mode,只覆盖 SELECT 出现的 dt,不动其他历史分区)
|
|
|
|
|
+ - **N 按业务实测 delta_days 分布 P95~P99 选定**,候选 N=5 / 7 / 14
|
|
|
|
|
+ - **配套机制**:回算窗 N(防)+ 定期校验对账 Hive vs PG(查)**双保险**——订单类事实表业界标准做法
|
|
|
|
|
|
|
|
-- **决策**:DWD 事件表跑批回算近 N=2 日:
|
|
|
|
|
|
|
+- **业界 N 取值经验**(按业务场景,给团队新成员参考):
|
|
|
|
|
|
|
|
- - sche sched=T 时,扫 `ods.dt IN (${dt}, ${pdt})`(即 T-1 + T-2)
|
|
|
|
|
- - 过滤业务时间 `DATE(business_event_time) IN (${dt}, ${pdt})`(如 dwd_pay 用 `payment_success_time`)
|
|
|
|
|
- - 写入 dwd `dt IN (${dt}, ${pdt})`:`INSERT OVERWRITE PARTITION (dt)` 动态分区,kb/26 §8 项目默认 DYNAMIC mode 只覆盖 SELECT 出现的 dt,不动其他历史分区
|
|
|
|
|
- - `dwd.dt=T-1` 在 sched=T 跑批时首次写入;sched=T+1 跑批时通过扫 `ods.dt=T` 兜回业务时间 T-1 漂移到 `ods.dt=T` 的事件,重写 `dwd.dt=T-1`
|
|
|
|
|
- - N=2 业界主流(阿里 OneData / 字节 / 美团默认)
|
|
|
|
|
|
|
+ | 业务场景 | 业界 N |
|
|
|
|
|
+ |---|---|
|
|
|
|
|
+ | 维度表 / 通用聚合 / 短生命周期事件 | N=2~3 |
|
|
|
|
|
+ | 订单 / 交易 / 物流(长状态机) | N=5~7 |
|
|
|
|
|
+ | 金融退款 / 法律审批 / 极长尾 | N=14~30 |
|
|
|
|
|
+
|
|
|
|
|
+ **N 不是 universal 默认值,由业务漂移分布决定**。实测 `delta_days = DATE(update_time) - DATE(business_event_time)` 分布,取 P95~P99 即得 N。早期 N=2 的取值是按"维度表 / 通用聚合"档位拿来直接套订单事实表,是经验失配。
|
|
|
|
|
+
|
|
|
|
|
+- **权威来源**:
|
|
|
|
|
+
|
|
|
|
|
+ - **Kimball 维度建模理论**(Ralph Kimball, _The Data Warehouse Toolkit: The Definitive Guide to Dimensional Modeling_, 3rd ed., Wiley, 2013)
|
|
|
|
|
+ - 第 3 章 Retail Sales:事实表按业务时间分区原则
|
|
|
|
|
+ - 第 12 章 Late-Arriving Facts:迟到事实的回算窗 N 天处理范式
|
|
|
|
|
+ - **阿里巴巴数据中台方法论**(《阿里巴巴大数据之路:数据中台之 OneData》第 11 章 事实表设计):DWD 事实表按业务时间分区 + 回算窗设计
|
|
|
|
|
+ - **现代湖仓引擎对照**:Apache Hudi / Iceberg / Delta Lake 的 `MERGE INTO` 是回算窗的引擎级替代,需 Spark 3+
|
|
|
|
|
+
|
|
|
|
|
+- **实测 SQL**(决策前必跑):
|
|
|
|
|
+
|
|
|
|
|
+ ```sql
|
|
|
|
|
+ SELECT (DATE(update_time) - DATE(payment_success_time)) AS delta_days,
|
|
|
|
|
+ COUNT(*) AS cnt
|
|
|
|
|
+ FROM public.card_group_order_info
|
|
|
|
|
+ WHERE payment_success_time >= '2026-01-01'
|
|
|
|
|
+ AND payment_success_time < '2026-05-01' -- 取已稳定的老数据
|
|
|
|
|
+ AND status IN (101, 103, 104, 105, 106, 301, 302)
|
|
|
|
|
+ GROUP BY (DATE(update_time) - DATE(payment_success_time))
|
|
|
|
|
+ ORDER BY delta_days;
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+ 按 P95~P99 取 N。
|
|
|
|
|
|
|
|
- **后果**:
|
|
- **后果**:
|
|
|
|
|
|
|
|
- 正面:
|
|
- 正面:
|
|
|
- - 兜底跨零点 ods 漂移(业务库 `update_time` 不严格同步刷新场景)
|
|
|
|
|
|
|
+ - cover 短期跨零点 + 长期业务状态机两类漂移
|
|
|
|
|
+ - 与业界订单类事实表设计对齐
|
|
|
- 不依赖 OLTP 应用层契约的强假设
|
|
- 不依赖 OLTP 应用层契约的强假设
|
|
|
- - 与 kb/20 §7.3 通用兜底一致
|
|
|
|
|
- 负面:
|
|
- 负面:
|
|
|
- - 每个 dt 分区被回算 2 次(首次 + 次日兜底),写入 / 计算成本上升 2×
|
|
|
|
|
|
|
+ - 每次 dwd sche 扫 ods 数据量增加 N/2 倍(N=7 时 3.5x);写入分区数同步扩大
|
|
|
- 动态分区写入需注意 dynamic mode 默认行为(kb/26 §8 已实测)
|
|
- 动态分区写入需注意 dynamic mode 默认行为(kb/26 §8 已实测)
|
|
|
|
|
|
|
|
- **候选方案**:
|
|
- **候选方案**:
|
|
|
|
|
|
|
|
- - **N=1 单分区不回算**(5/9 一度采纳):基于"业务库 update_time 与业务事件时间同步刷新 OLTP 契约"假设;OLTP 后端实际不可信,跨零点漂移会永久丢失事件——否决
|
|
|
|
|
- - **N=3 回算近 3 日**:偏保守,金融 / 强稳定性场景;本项目业务侧无此强稳定性需求,2× 写入对齐业界默认即可——否决但留作"业务库延迟 > 1 天频繁时上调"反悔触发
|
|
|
|
|
- - **N=7 回算近 7 日**:极端保守,少见——否决
|
|
|
|
|
- - **MERGE INTO**(Spark 3+ / Iceberg / Hudi / Delta Lake):不需回算逻辑,引擎原生 upsert 处理漂移——本项目 Spark 2.4 不支持,本阶段不取
|
|
|
|
|
- - **延后 1 天跑批**(sched=T+1 写 dwd.dt=T-1,扫 ods.dt IN (T-1, T)):dwd 数据延迟 1 天可用,下游标签延迟,业务侧不可接受——否决
|
|
|
|
|
|
|
+ - **N=1 单分区不回算**(5/9 一度采纳):基于"OLTP 严格同步刷新"强假设,业务库实际不可信,跨零点漂移会永久丢失事件——否决
|
|
|
|
|
+ - **N=2 跨零点兜底**(2026-05-10 一度采纳):cover 短期跨零点漂移 OK,未 cover 业务状态机长生命周期——本 ADR 重评估
|
|
|
|
|
+ - **N=5~7 订单类**:cover P95~P99 漂移,业界订单类常见取值
|
|
|
|
|
+ - **N=14~30 极长尾**:cover 退款窗(电商 7-14 天)+ 法律周期;本项目订单场景过保守
|
|
|
|
|
+ - **MERGE INTO**(Spark 3+ / Iceberg / Hudi / Delta Lake):不需回算窗,引擎原生 upsert;Spark 2.4 不支持,本阶段不取
|
|
|
|
|
+ - **延后 1 天跑批**(sched=T+1 写 `dwd.dt=T-1`,扫 `ods.dt IN (T-1, T)`):下游标签延迟 1 天,业务侧不可接受——否决
|
|
|
|
|
+ - **跳过 raw/ods 直接从 PG 灌 dwd**(一次性 manual SQL):绕过链路 + 技术债——否决
|
|
|
|
|
|
|
|
- **反悔条件**:
|
|
- **反悔条件**:
|
|
|
|
|
|
|
|
- - 业务库延迟 > 1 天的漂移场景频繁出现(如系统升级 / 故障恢复多日数据回流),N 上调到 3 或更大
|
|
|
|
|
- - 迁 Spark 3+ / Iceberg / Hudi / Delta Lake 后改 MERGE INTO(更优)
|
|
|
|
|
- - 业务侧能严格保证 OLTP 应用层契约(update_time 与事件时间同步刷新),可降到 N=1(实际很难保证)
|
|
|
|
|
|
|
+ - 业务漂移分布显著变化(业务流程改造)→ 重跑实测 SQL 调 N
|
|
|
|
|
+ - 数据量增长后 N 天回算扫描成本不可接受 → 降 N + 加重定期校验频率 / 阈值
|
|
|
|
|
+ - 迁 Spark 3+ / Iceberg / Hudi / Delta Lake → 改 `MERGE INTO`(更优)
|
|
|
|
|
+ - 业务库能严格保证 OLTP 应用层契约(`update_time` 与事件时间同步刷新)→ 降到 N=1(实际很难保证)
|
|
|
|
|
+
|
|
|
|
|
+- **实施依赖**(落定 N 后执行):
|
|
|
|
|
+
|
|
|
|
|
+ - 跑 PG SQL 拿 delta_days 分布定 N
|
|
|
|
|
+ - 改 `jobs/dwd/trd/dwd_trd_order_pay_apd_d.sql` 回算窗 2 处过滤(`ods.dt` + `payment_success_time`)同步扩大
|
|
|
|
|
+ - 重跑 dwd 补数验证缺数回归
|
|
|
|
|
+ - 定期校验脚本(GMV + count 对账,复用 `workspace/20260527/gmv_check.py`)扩成 DS 周校验工作流
|
|
|
|
|
|
|
|
### ADR-10 TDM 跨层下钻 DWD(1 期专用,dws 单下游场景)
|
|
### ADR-10 TDM 跨层下钻 DWD(1 期专用,dws 单下游场景)
|
|
|
|
|
|