Просмотр исходного кода

docs(kb): 20 §7 并入 inbox 分层分区语义文档

新增三个子节:
- §7.2 各层分区语义(含分区语义总览表 + 漂移/重复容忍概念)
- §7.3 各层职责与设计要点(Raw 抽取宽窗、ODS 两种写入模式、
  DIM 选型判据、DWS 增量优化、ADS 可重放性等)
- §7.4 6 条分区与建模设计原则

inbox 文档原文保留不删
tianyu.chu 1 неделя назад
Родитель
Сommit
e57525067e
1 измененных файлов с 144 добавлено и 0 удалено
  1. 144 0
      kb/20-数仓分层与建模.md

+ 144 - 0
kb/20-数仓分层与建模.md

@@ -199,6 +199,150 @@ RDS PG / ES ──DataX──▶ RAW ──SparkSQL──▶ ODS ──▶ DWD 
 
 **拼团表 DWD 快照策略**:每日增量快照 + 近 7 天全量快照 + 全量拉链表
 
+### 7.2 各层分区语义
+
+各层 `dt` 分区键代表的时间含义、对漂移和重复的容忍度、分区间关系不同,是设计 ETL 任务和理解查询行为的基础。
+
+| 层 | 分区键语义 | 时间字段来源 | 漂移容忍 | 重复容忍 | 分区间关系 |
+|---|---|---|---|---|---|
+| Raw | 批次日(系统时间) | `dt = stop - 1`(抽取任务的逻辑日期) | **容忍** | **容忍**(同 pk 同 dt 内可多条) | 独立,可含跨日漂移数据 |
+| ODS | 记录最后写入日(系统时间) | `DATE(update_time)` | 不容忍 | 分区内不容忍、**跨分区容忍**(保留 `update_time` 轨迹) | 独立,同 pk 可跨多分区 |
+| DWD 事实表 | 业务行为发生日(业务时间) | `DATE(order_create_time)` / `DATE(event_time)` 等业务字段 | 不容忍 | 不容忍(事件不可变) | 独立,追加写 |
+| DIM 拉链表 | 不按时间分区(或 `is_current` 二级分区) | —— | —— | **多版本非重复**(`[start_date, end_date)` 区间不重叠) | 每行是状态生效区间 |
+| DIM 快照表 | 快照日(业务时间) | `dt = today` | 不容忍 | 分区内不容忍;跨分区同 pk 多次出现是"每日快照"而非重复 | 独立,每分区是该日全量 |
+| DWS | 统计截止日(业务时间) | 聚合口径的截止点 | 不容忍 | 分区内不容忍;分区间原始明细冗余 | **分区间冗余**(滑动窗口重叠) |
+| ADS | 报表快照日(业务时间) | 报表生成日 | 不容忍 | 不容忍 | 独立,一行可含多统计周期指标 |
+
+**两个容忍度的概念:**
+
+- **漂移容忍** = "某条记录落在了错误的 dt 分区里,能不能接受?" 描述**数据 vs 分区归属**的正确性
+  - Raw 容忍:9 号 `update_time` 数据落在 Raw `dt=8 号` 没关系,下游会归位
+- **重复容忍** = "同一个 pk 在分区内或跨分区出现多次,能不能接受?" 描述**分区内 / 跨分区的唯一性**约束
+  - Raw 容忍:一次抽取窗口内同 pk 可能被捞多次,不需要去重
+  - ODS 分区内不容忍(需去重),跨 dt 容忍(保留轨迹)
+  - DWD 事实表双不容忍:事件就是一次,多一条就是错
+  - DIM 拉链表"多版本非重复":同 pk 多行表示状态演化
+
+**两个容忍度的组合反映各层的核心取舍:**
+
+- Raw 双容忍 → 抽取简单,绝不丢数据
+- ODS 漂移不容忍 + 跨分区重复容忍 → 归位 + 保留轨迹
+- DWD 事实表双不容忍 → 事件唯一,业务语义精确
+- DIM 拉链表特殊 → 多版本不是重复
+- DWS / ADS 双不容忍 + 分区间明细冗余 → 聚合结果唯一,存储冗余换查询速度
+
+### 7.3 各层职责与设计要点
+
+各层基础职责见 §2 表格;本节聚焦每层的关键设计取舍。
+
+#### Raw 层
+
+> 基础职责见 §2 + §8.1。
+
+- **写入窗口**:抽取窗口 `[day-start, day+1-stop)`(48 小时宽),所有抓到的记录统一落 `dt = stop - 1` 分区
+- **设计理由**:宽窗覆盖"零点漂移"和"覆盖式更新下的永久丢失",保证数据永不丢失
+- **代价**:分区里混有"未来时间"的记录 + 同 pk 可能重复出现,接受这两个代价换抽取逻辑简单
+
+#### ODS 层
+
+> 基础职责见 §2 + §8.2。
+
+- **写入**:Spark SQL 动态分区 `PARTITION (dt)`,按 `DATE(update_time)` 分发,把 Raw 漂移数据归位到正确分区
+- **两种写入模式**(可表级混用):
+  - **方案 A(INSERT OVERWRITE)**:每次 ODS 跑批覆盖对应 dt 分区;数据不丢失,因为 Raw 最多漂一天,次日 Raw 必然抓到
+  - **方案 B(INSERT INTO + 分区内 `(pk, max(update_time))` 去重)**:保留每日 ODS 跑时刻的 `dt=X` 版本轨迹,用于审计、回溯,防止覆盖后丢失中间快照
+- **关键约束**:**跨 dt 不去重**。同一 pk 允许在多个 dt 分区并存,每条代表一个"时间段状态快照"——是上层 DIM 拉链表(SCD Type 2)的必要基础
+
+#### DWD 层(事实明细)
+
+> 基础职责 + 事件 vs 状态拆分原则见 §5.5。
+
+- **分区**:业务时间(下单日、支付日、事件发生日),不是抽取日
+- **写入**:每天**冗余跑近 3 日**,兜底 ODS 漂移(虽然 ODS 已归位,但 ODS 漂移修正后 DWD 需要回算)
+
+#### DIM 层(维度)
+
+- **职责**:承载业务实体的状态
+- **建模**:按表特征选型,**不统一**
+
+| 表特征 | 建模方式 | 分区策略 | 写入模式 |
+|---|---|---|---|
+| 大 / 中维度表(用户、商品、商户) | **拉链表 SCD2** | 不分区(或 `is_current` 二级分区) | 当日变更 pk:原行 `end_date` 置昨天 + 新行 insert |
+| 小高频变更维表(类目、地区、配置) | **每日全量快照** | 业务时间分区 | 每日全量覆盖 `dt=today` 分区 |
+| 极小不变表(字典、枚举) | 单表全量 | 不分区 | 偶尔全量覆盖 |
+
+**选型判据:变更率 × 保留天数**
+
+- 变更率低(< 1% / 日)+ 保留长:拉链更优
+- 变更率高(> 20% / 日)+ 保留短:快照更优
+
+**拉链表分区的特殊性:**
+
+- 拉链表每行是"状态生效区间",不是"时间点",天然不适合按业务时间分区
+- 可以按 `is_current='Y'/'N'` 做二级分区加速"当前状态"查询
+- 大表可以按 `start_date` 年份 / 月份做粗粒度分区控制扫描范围
+
+#### DWS 层
+
+- **职责**:面向分析主题的轻度汇总,**用冗余存储换查询性能**
+- **组织方式**:`主题 × 粒度 × 统计周期` 一张表
+  - `dws_user_order_1d`、`dws_user_order_7d`、`dws_user_order_30d`
+  - `dws_shop_order_1d`、`dws_shop_order_7d`、`dws_shop_order_30d`
+- **分区语义**:业务截止日。每个分区自包含完整周期聚合(7 日表每天分区含过去 7 天完整聚合)
+- **分区间冗余是刻意设计**:滑动窗口天然重叠,换取"一次分区裁剪命中答案"的查询速度
+- **来源**:DWD 事实表 + DIM 维度表 join 聚合
+- **增量计算优化**:`7d = 今日 1d + 昨日 7d - 7 天前 1d`,减少 DWD 扫描压力
+
+#### ADS 层
+
+- **职责**:面向具体报表 / 应用,把多个统计周期拼成一行,BI 直接用
+- **组织方式**:`报表主题` 一张表,`一行多周期`
+  - `user_id | order_cnt_1d | order_cnt_7d | order_cnt_30d | gmv_1d | ... | dt`
+- **分区语义**:报表快照日(历史报表不可被后续数据改动,保审计性)
+- **生成方式**:多张 DWS 同分区日 join 而来
+- **可重放性**:所有 ADS 数据都能从 DWS / DWD / DIM 重算;实务中用 TTL 定期删除历史分区,要查老报表就触发任务重放
+
+### 7.4 分区与建模设计原则
+
+**原则 1:Raw / ODS 是系统时间,DWD 及以上是业务时间**
+
+- **Raw / ODS**:系统时间(`update_time` 及 ETL 批次时间)。贴源层不解释业务语义,只忠实反映"数据库里发生了什么"
+- **DWD / DIM / DWS / ADS**:业务时间(`order_create_time` 等)。面向分析,分区语义对齐业务问题("8 号下了多少单"指的是用户 8 号下单的行为,不是数仓 8 号抓到的数据)
+
+**原则 2:漂移容忍度和重复容忍度自下而上递减**
+
+- Raw:双容忍(漂移 + 重复都接受)
+- ODS:漂移不容忍、分区内重复不容忍、跨分区重复容忍(保留 `update_time` 轨迹)
+- DWD / DIM / DWS / ADS:漂移不容忍、分区内重复不容忍(DIM 拉链的"多版本"不算重复)
+
+**原则 3:分区语义和查询语义对齐**
+
+查询条件里的 `where dt='8 号'` 应该和用户的直觉对齐:
+
+- 分析师问"8 号下单数" → 查 DWD 事实表 `where dt='8 号'`,分区键是业务下单日
+- 运维排查"8 号这批抓了什么" → 查 Raw `where dt='8 号'`,分区键是批次日
+
+两种"8 号"语义不同,分别由不同层承载,不混淆。
+
+**原则 4:事实与维度解耦(DWD vs DIM 并列)**
+
+- **DWD 事实表**:记录不可变业务事件,只追加,业务时间分区
+- **DIM 维度表**:记录实体状态,拉链或快照,按规模和变更率选型
+- 跨表计算(下单数 - 退单数 = 实际下单数)是维度建模的**正常能力**,不是代价
+- 不把两者混在一层 → 建模方法、分区策略、更新方式清晰分离
+
+**原则 5:ODS 为下游保留最大灵活性**
+
+- ODS 跨 dt 不去重 → 保留 `update_time` 轨迹 → 支持 DIM 拉链表建设
+- ODS 不预设下游建模方式 → DWD 可按表特征自由选事件化,DIM 可按规模选拉链 / 快照
+
+**原则 6:上层数据可重放,下层数据不可重放**
+
+- **Raw / ODS**:源头层,业务库覆盖式更新会导致历史无法还原,**不可重放**,要长期保留
+- **DWD / DIM**:规整层,理论可从 ODS 重放,实务按"不可重放"管理,长期保留
+- **DWS / ADS**:派生层,可重放(前提上游数据还在),用 TTL 控制存储成本
+- **保留周期自下而上递减**(越接近派生数据的层,TTL 越短)
+
 ## 8. raw 层与 ods 层的职责约定
 
 这是本数仓的核心数据契约,所有 raw / ods 层作业都必须遵守。