Эх сурвалжийг харах

docs(kb): 93 加 ADR-05 DWD 事件 vs 状态拆派

tianyu.chu 1 долоо хоног өмнө
parent
commit
d198808f4c
1 өөрчлөгдсөн 43 нэмэгдсэн , 0 устгасан
  1. 43 0
      kb/93-架构决策.md

+ 43 - 0
kb/93-架构决策.md

@@ -113,3 +113,46 @@
   - CLI 用合成 flag `--datax-conf key=value,...`——否决,speed 高频用独立 flag 更直观,Spark 入口风格参考
 
 - **反悔条件**:三级注入使用场景长期 <5%(即 L2 L3 基本不用),退回单层 conf;或者 DataX 被替换
+
+### ADR-05 DWD 事实表设计:默认"事件 vs 状态"拆派组合,acc 累积快照仅留例外口子
+
+- **状态**:已采纳
+
+  老 `kb/20 §5.5` 走"合 vs 拆"二分法(粒度相同则合粒度不同则拆),举例订单履约 / 拼团生命周期均"合到 acc 累积快照"——一行一实体,每个里程碑一列时间戳,状态单向推进。该写法属 Kimball 维度建模理论里的 Accumulating Snapshot Fact Table,在传统数仓(Teradata / Oracle / Greenplum)里是经典模式。
+
+  2026-04-26 启动埋点 + 业务库接入业务建模时重新审视,发现两个核心问题:
+
+  1. **Hive 技术栈不友好**:Hive ORC 普通表不支持原生 UPDATE,acc 模式(每经过里程碑回写时间戳列)只能"INSERT OVERWRITE 整张表 / 整个分区"实现,每天扫近 N 天 ODS 重算,写入开销大、性能粗暴。要解此约束需开 Hive ACID(CDH 6 支持但少用 + 性能折损)或上 Iceberg / Hudi / Delta Lake,本项目都不具备
+  2. **业务流程多有循环**:拼团有审核机制(提交 → 拒绝 → 重新提交 → 再审核),订单亦有退款 / 重派单等循环。Kimball 对 acc 的前提是"每个状态只经过一次"——循环场景 acc 的时间戳列会丢失历史,不适用
+
+  期间在 inbox 收了一篇分层分区语义草稿,主张"事实表只承载事件、状态归 DIM 拉链"。讨论后认为该结论方向正确,但论证过度(直接否定 Kimball 三类事实表的全部);最终采纳折中表述:默认拆派 + 留 acc 例外口子。
+
+- **决策**:默认采用"事件 vs 状态"拆派组合:
+
+  - **DWD 事实表只承载业务事件**(不可变事实),命名 `dwd_{域}_{业务过程}_apd_d`,append-only,业务时间分区
+  - **实体当前状态进 DIM 拉链表**,命名 `dim_{域}_{实体}_zip_d`,SCD Type 2 每次状态变更生成新行
+  - 循环状态机天然支持:事件流水任意次重复追加,DIM 拉链每次变更生成新行可完整回溯
+  - **acc 不一刀切禁用**:极少数严格不循环、固定线性里程碑场景(如不可逆的合同审批流程)按需评估,新建 acc 表时在 PR / 设计稿里说明选型理由
+
+- **后果**:
+
+  - 正面:
+    - 与 Hive 列存"只追加 + 分区"模型天然契合,不需要 UPDATE
+    - 状态查询走拉链表 `is_current` 切片或按 dt 切片,直观
+    - 循环 / 单向状态机统一同一套建模,降低团队心智负担
+    - 与阿里 OneData / 字节 / 美团 Hive 数仓主流做法对齐
+  - 负面:
+    - 跨表 JOIN("下单数 - 退单数 = 实际下单数")成为常态,但维度建模本就支持
+    - DIM 拉链表实现复杂度高于 acc 单表(每日变更 pk 检测 + 原行 `end_date` 置昨天 + 新行 insert)
+    - 状态查询需要 join dim 拉链而非读单表,少量查询复杂度
+
+- **候选方案**:
+
+  - **保留老 §5.5 合派 acc**:经典 Kimball 但不适合 Hive 技术栈 + 不支持循环——否决
+  - **完全禁用 acc**:inbox 草稿原版主张"事实表出现需要更新状态就是建模错"——过度扩大化,放弃了 Kimball 三类事实表的理论丰富性,未来引擎栈升级(Iceberg / Hudi)想用 acc 又得反悔——否决,留例外口子
+  - **inc 主表 + apd 流水表 fallback**(kb/21 §2.2 老循环 fallback 方案):inc 只保留最新状态,状态历史靠 apd 流水还原,但状态查询要 join 流水表算最新值,远不如拉链表 `is_current` 直接——否决
+
+- **反悔条件**:
+  - 引擎栈升级到支持高效 UPDATE 的存储(Iceberg / Hudi / Delta Lake),acc 实现成本 / 性能不再是问题,可重新评估
+  - DIM 拉链表实现复杂度成为团队瓶颈(开发提效需要)
+  - 实际场景中跨表 JOIN 性能 / 维护成本超出预期