Bladeren bron

docs(kb): 建模族去重对齐 — 20 瘦身 / ods 并入 22 / 各层去重 + 索引重排

- README 索引按 6 语义带重排(命名/指标/标签归位 3x/4x)+ 修 raw 系显示号
- 20:§7.3 各层细则砍成指针表、§8 raw/ods 契约整节移入 22、§5.2 收口加工层框架字段
- 22:新增 §0 raw/ods 通用约定(全 STRING / 类型化脏数据边界 / 类型映射指向 ADR-06)
- 23/24/25:框架字段改指针、术语统一(类型错误 vs 值脏归一)、DWD 回算窗 N=2→滚动 30、DWS 跟到滚动 30(实现待改)
- 21 §6:回填 dwd/dws dt 语义
- 修 §8 死链 9 处(kb/12/13/14/41 + manual/tests SQL)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tianyu.chu 1 week geleden
bovenliggende
commit
7168e5424e

+ 19 - 14
README.md

@@ -60,7 +60,7 @@ PG/ES ──DataX(raw)──> RAW ──> ODS ──> DWD ──> DWS ──> TD
 
 文件按编号前缀分组,序号越小越基础、越靠前阅读。每篇文档是其主题的**唯一权威**(SSOT),跨文档用 `§` 锚点互引、不复述。
 
-### 0x 项目与环境
+### 0x 平台基建
 
 | 文档 | 职责 |
 |----|----|
@@ -68,7 +68,7 @@ PG/ES ──DataX(raw)──> RAW ──> ODS ──> DWD ──> DWS ──> TD
 | [01-运行环境](kb/01-运行环境.md) | **基础设施**:CDH 版本矩阵、集群拓扑、开发侧约束 |
 | [02-权限与账号](kb/02-权限与账号.md) | **鉴权链路**:HS2 doAs / PySpark / Ranger / LDAP;job 账号 vs 个人账号 |
 
-### 1x 业务上下文
+### 1x 业务与接入
 
 | 文档 | 职责 |
 |----|----|
@@ -77,29 +77,34 @@ PG/ES ──DataX(raw)──> RAW ──> ODS ──> DWD ──> DWS ──> TD
 | [12-同步方案](kb/12-同步方案.md) | **同步策略**:PG→Hive 存量 / 增量 / 历史归档 / CDC 阶段演进 |
 | [13-埋点同步-设计](kb/13-埋点同步-设计.md) | **埋点同步设计**:合规约束、整体方案、脱敏策略、协作流程(受众:产品 / CTO) |
 | [14-埋点同步-开发](kb/14-埋点同步-开发.md) | **埋点同步实现**:raw DDL、脱敏配置、历史 / 增量入仓脚本、落地 checklist |
+| [15-raw接入流程](kb/15-raw接入流程.md) | **新表入仓标准 8 步**:sync 生成器 + mask 配置 + DDL 生成器 + raw/ods 建模文档协同 |
 
 ### 2x 数仓建模
 
 | 文档 | 职责 |
 |----|----|
-| [20-数仓分层与建模](kb/20-数仓分层与建模.md) | **建模方法论**:分层定义、主题域、总线矩阵、维度五步法 |
-| [40-命名规范](kb/40-命名规范.md) | **命名规则**:Hive 表 / 字段 / 词根字典 + 建表 Checklist |
+| [20-数仓分层与建模](kb/20-数仓分层与建模.md) | **建模方法论**:分层定义、主题域、总线矩阵、维度五步法、分区与建模原则 |
+| [21-时间语义](kb/21-时间语义.md) | **时间变量约定**:T 任务日锚点 + DS 变量底层 + cdt/dt/tdt/pdt 项目参数 + 各层 dt 语义 + 重跑幂等条件 |
+| [22-raw / ods 建模](kb/22-raw建模.md) | **raw/ods 层约定 + raw 源表字段目录**:raw 全 STRING landing、ods 类型化/脏数据边界、各源表三态裁剪决策 |
+| [23-dwd建模](kb/23-dwd建模.md) | **DWD 字段建模**:业务过程拆分 + 维度退化策略 + 滚动 N=30 回算 + 各业务过程字段表 |
+| [24-dim建模](kb/24-dim建模.md) | **DIM 字段建模**:ful_d 优先选型 + ful→zip 触发条件 + 跑批策略 + 脏数据清洗位置 + 各实体字段表 |
+| [25-dws建模](kb/25-dws建模.md) | **DWS 字段建模**:日聚合主题宽表单一职责 + 维度退化触发条件 + 滚动 30 回算(与 DWD 对齐)+ 字段表 |
+| [26-tdm建模](kb/26-tdm建模.md) | **TDM 字段建模**:EAV 7 字段不扩 + _d 日更 / _o 凝固分表 + tag_code 命名 + 16 品类 × 4 窗口消费明细 + 跨年扩张 |
+
+### 3x 指标与标签
+
+| 文档 | 职责 |
+|----|----|
 | [30-指标体系](kb/30-指标体系.md) | **指标字典**:指标 / 维度 / 度量定义与口径 |
-| [31-标签体系](kb/31-标签体系.md) | **TDM 画像**:用户 / 商品 / 商家标签 |
-| [24-raw 建模](kb/22-raw建模.md) | **raw 层字段裁剪决策**:各 raw 表三态标记(保留 / 明性裁 / 隐性裁)+ 决策理由溯源 |
-| [25-raw 接入流程](kb/15-raw接入流程.md) | **新表入仓标准 8 步**:sync 生成器 + mask 配置 + DDL 生成器 + raw 建模文档协同 |
-| [21-时间语义](kb/21-时间语义.md) | **时间变量约定**:T 任务日锚点 + DS 变量底层 + cdt/dt/tdt/pdt 项目参数 + raw/ods 各层 dt 语义 + 重跑幂等条件 |
-| [27-dwd 建模](kb/23-dwd建模.md) | **DWD 字段建模**:业务过程拆分 + 维度退化策略 + 单分区不回算 + 各业务过程字段表 |
-| [28-dim 建模](kb/24-dim建模.md) | **DIM 字段建模**:ful_d 优先选型 + ful→zip 触发条件 + 跑批策略(初始化 + 增量合并)+ 脏数据清洗位置 + 各实体字段表 |
-| [29-dws 建模](kb/25-dws建模.md) | **DWS 字段建模**:日聚合主题宽表单一职责 + 维度退化触发条件 + N=2 回算(与 DWD 对齐)+ dws_usr_user_trade_1d 字段表 |
-| [33-tdm 建模](kb/26-tdm建模.md) | **TDM 字段建模**:EAV 7 字段不扩 + _d 日更 / _o 一次性凝固分表 + tag_code 命名约定 + 16 品类 × 4 窗口消费明细统计 + 7 属性 + 跨年扩张规则 |
+| [31-标签体系](kb/31-标签体系.md) | **TDM 画像**:用户 / 商品 / 商家标签体系架构 |
 | [32-标签字典](kb/32-标签字典.md) | **标签字典权威源**:103 标签全量(编码 / 口径 / 业务价值 / 示例)+ 编码规则 + 跨年滚动 + INTERSECT 圈选 SQL 示例(读者:运营 / 数据分析师)|
 
-### 3x 开发流程
+### 4x 规范与工具
 
 | 文档 | 职责 |
 |----|----|
-| [41-开发规范](kb/41-开发规范.md) | **开发方法论**:TPAD、数据开发流程、代码/SQL/Git 规范、DDL/jobs 文件组织、manual/ 临时 SQL、样板、存储 / raw 类型契约 |
+| [40-命名规范](kb/40-命名规范.md) | **命名规则**:Hive 表 / 字段 / 词根字典 + 建表 Checklist |
+| [41-开发规范](kb/41-开发规范.md) | **开发方法论**:TPAD、数据开发流程、代码/SQL/Git 规范、DDL/jobs 文件组织、manual/ 临时 SQL、样板 |
 | [42-UDF手册](kb/42-UDF手册.md) | **UDF 登记表**:通用 + 业务 UDF 清单 |
 
 ### 9x 演进与决策

+ 1 - 1
kb/12-同步方案.md

@@ -116,4 +116,4 @@
 
 - [数据资产](11-数据资产.md) — 业务库与埋点数据源清单
 - [数仓分层与建模](20-数仓分层与建模.md) §6 — 数据同步策略、快照类型决策
-- [数仓分层与建模](20-数仓分层与建模.md) §8 — raw / ods 契约、类型映射参考
+- [raw / ods 建模](22-raw建模.md) §0 — raw / ods 层契约(类型映射见 ADR-06)

+ 1 - 1
kb/13-埋点同步-设计.md

@@ -46,7 +46,7 @@
 
 数仓默认 raw 层走 schema-on-read landing 范式(数据原样落、ods 阶段才做类型转换 / 脏数据识别)。本场景**主动破例**:
 
-| §8.1 理由 | 是否适用 | 说明 |
+| `22` §0.1 理由 | 是否适用 | 说明 |
 |---|---|---|
 | 1. 隔离源端类型变化 | ✗ | 埋点 schema 长尾,新事件 / 新字段频繁,要求协作通知 |
 | 2. 同步阶段不可失败 | △ | 部分适用,需对解析失败做容错处理 |

+ 1 - 1
kb/14-埋点同步-开发.md

@@ -61,7 +61,7 @@ LOCATION '/user/hive/warehouse/raw.db/raw_usr_traces_apd_d';
 
 DDL 文件落点:`manual/ddl/raw/usr/raw_usr_traces_apd_d_create.sql` + `manual/ddl/raw/usr/raw_usr_traces_his_o_create.sql`。`raw.raw_usr_traces_his_o` schema 同上,仅表名和 LOCATION(`/user/hive/warehouse/raw.db/raw_usr_traces_his_o`)不同。
 
-> **与 §8.1 default ORC 一致;与现 testbed `test.raw_usr_traces_apd_d` 的单列 STRING + TEXTFILE 不同**——testbed 是冒烟阶段产物,本方案不复用其 schema。
+> **与 `22-raw建模` §0.1 default ORC 一致;与现 testbed `test.raw_usr_traces_apd_d` 的单列 STRING + TEXTFILE 不同**——testbed 是冒烟阶段产物,本方案不复用其 schema。
 
 ## 2. 脱敏配置
 

+ 3 - 3
kb/15-raw接入流程.md

@@ -36,7 +36,7 @@
 ┌──────────────────┐
-│ Step 6:md 同步   │ 字段表复制到 kb/24-raw 建模.md 对应章节
+│ Step 6:md 同步   │ 字段表复制到 kb/22-raw建模.md 对应章节
 │ 到建模文档        │
 └────────┬─────────┘
@@ -131,7 +131,7 @@ python3 bin/datax-sync-template-gen.py \
 
 ### Step 6:md 同步到 raw 建模文档
 
-复制 workspace `.md` 字段表 → `kb/24-raw 建模.md` 对应表章节。**人工维护**——后期 raw 建模文档由人类驱动更新,不做脚本自动追加。
+复制 workspace `.md` 字段表 → `kb/22-raw建模.md` 对应表章节。**人工维护**——后期 raw 建模文档由人类驱动更新,不做脚本自动追加。
 
 ### Step 7:跑 DDL 生成器 + 微调
 
@@ -150,7 +150,7 @@ python3 bin/hive-ddl-gen.py -l raw -ini jobs/raw/{域}/{table_name}.ini -o
 - `jobs/raw/{域}/{table_name}.mask.ini`(裁剪脱敏配置)
 - `jobs/raw/{域}/{table_name}.ini`(sync 配置)
 - `manual/ddl/raw/{域}/{table_name}_create.sql`(raw 建表 DDL)
-- `kb/24-raw 建模.md` 对应表章节(字段三态决策文档化)
+- `kb/22-raw建模.md` 对应表章节(字段三态决策文档化)
 
 ## 暂未实现的阶段(后期)
 

+ 10 - 142
kb/20-数仓分层与建模.md

@@ -105,6 +105,7 @@ RDS PG / ES ──DataX──▶ RAW ──SparkSQL──▶ ODS ──▶ DWD 
 - **维度退化**:将常用维度属性冗余到事实表,减少运行时 JOIN。
 - **一致性维度**:公共维度全局统一(见 `dim_pub_*` 系列)。
 - **原子粒度优先**:最低粒度事实可以无限上卷,高粒度事实不可下钻。
+- **加工层框架字段**:DWD / DIM / DWS / TDM 表统一带 `etl_time TIMESTAMP` + 分区 `dt STRING` + `STORED AS ORC`;raw / ods 字段与类型约定见 `22-raw建模.md` §0。
 
 ### 5.3 建模示例
 
@@ -233,74 +234,16 @@ RDS PG / ES ──DataX──▶ RAW ──SparkSQL──▶ ODS ──▶ DWD 
 
 ### 7.3 各层职责与设计要点
 
-各层基础职责见 §2 表格;本节聚焦每层的关键设计取舍
+各层基础职责见 §2;写入模式 / 回算窗 / 选型等操作细则见各层建模文档,本节只留指针,不与层文档重复
 
-#### Raw 层
-
-> 基础职责见 §2 + §8.1。
-
-- **写入窗口**:抽取窗口 `[day-start, day+1-stop)`(48 小时宽),所有抓到的记录统一落 `dt = start_date`(业务日)分区
-- **设计理由**:宽窗覆盖"零点漂移"和"覆盖式更新下的永久丢失",保证数据永不丢失
-- **代价**:分区里混有"未来时间"的记录 + 同 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 定期删除历史分区,要查老报表就触发任务重放
+| 层 | 关键设计取舍 | 详见 |
+|---|---|---|
+| Raw | 48h 宽窗抓取、全落 `dt=start_date`、容忍漂移 + 重复 | `22-raw建模` §0.1 · `21-时间语义` §4 · ADR-03 |
+| ODS | 动态分区按 `update_time` 归位、双源 union、跨 dt 不去重、INSERT OVERWRITE | `22-raw建模` §0.2 · `21-时间语义` §5 · ADR-03 |
+| DWD | 业务时间分区、滚动 N=30 回算、事件 vs 状态拆分(见 §5.5) | `23-dwd建模` · ADR-09 / ADR-11 |
+| DIM | 状态承载、`ful_d` 默认 / `zip_d` 按需、拉链不按业务时间分区 | `24-dim建模` · ADR-08 |
+| DWS | 主题 × 粒度日聚合、冗余换查询性能、维度退化 | `25-dws建模` · ADR-10 |
+| ADS | 多周期拼一行、报表快照日分区、可从 DWS/DWD/DIM 重放 | (ADS 文档待建) |
 
 ### 7.4 分区与建模设计原则
 
@@ -343,81 +286,6 @@ RDS PG / ES ──DataX──▶ RAW ──SparkSQL──▶ ODS ──▶ DWD 
 - **DWS / ADS**:派生层,可重放(前提上游数据还在),用 TTL 控制存储成本
 - **保留周期自下而上递减**(越接近派生数据的层,TTL 越短)
 
-## 8. raw 层与 ods 层的职责约定
-
-这是本数仓的核心数据契约,所有 raw / ods 层作业都必须遵守。
-
-### 8.1 raw 层:schema-on-read landing
-
-> schema-on-read = 数据写入时不解释类型,读取时再按需解析。这是小数仓 / 列存 / 数据湖共用的范式,与传统 RDBMS 的 schema-on-write 形成对照。
-
-**全 STRING 的设计理由:**
-
-1. **隔离源端类型变化**:源系统改字段类型(`int4` → `bigint`、`varchar` → `text`)raw 入库零影响;类型解释下移 ods,源端变化只动 ods CAST 表。raw 表 schema 长期稳定,避免反复 ALTER 触发 metastore 高频变更
-2. **同步阶段不可失败**:raw 是回源链路兜底层,类型转换中单条脏数据可能让任务整个失败(如某行 `int4` 字段含 `'N/A'` → CAST 异常);STRING 永不失败,全量入仓后再到 ods 拦截脏数据
-3. **保留原始精度与原文**:CAST 可能丢精度(`NUMERIC(38,18)` → `DECIMAL(20,4)` 截断)、丢时区(`timestamptz` 反序列化)、改格式(日期字符串 `'20260101'` 反序列化后再格式化可能不一致);STRING 原汁原味,ods 想怎么解析都行
-4. **脏数据可观测**:业务库历史脏数据(`'1900-00-00'` 日期、`'-1'` 状态、超长字段)必须先入仓再观测;类型化阶段静默丢弃 / 报错跳过就再也看不到。STRING 100% 入仓 + ods 显式标记 / 分流(见 §8.2 脏数据拦截线)
-5. **schema-on-read 范式契合**:小数仓 / 列存(ORC)读取时类型解析成本极低,所以 raw 做 schema-on-read landing + ods 做 schema-on-write 是标准分工;vs 传统 OLTP 的 schema-on-write 必须入库时定型,灵活性差
-
-**何时可破例**:上述理由是工程权衡,不是教条。某个 raw 表场景如果这 5 条 ≥3 条不适用,可单独评估破例方式(如:数据源本就是 self-describing 的 NDJSON / Parquet / Avro 时保留单列裸文本 / 自带类型直存;schema 由上游严控且无脏数据风险时直接 typed 列入仓)。破例必须在 PR / 设计稿明示理由,不是默认。
-
-**规则:**
-
-- **全字段 STRING**:raw 层所有表业务字段以及 `dt` 分区字段一律 `STRING` 类型
-- **同步任务不做类型转换**:DataX ini 里不写 `columnType` 的类型映射(或统一填 `string`),CSV 导入时 SparkSQL 读取后也不 `CAST`
-- **外部表兜底**:raw 层建表一律用 `CREATE EXTERNAL TABLE`,DROP TABLE 只删元数据,HDFS 数据保留;raw 作为链路兜底层,误删元数据时数据仍可 `MSCK REPAIR` / 重建元数据恢复,无需回源库重同步
-
-### 8.2 ods 层:类型转换与脏数据识别
-
-- **ods 是类型化的第一层**:从 raw 的 STRING 字段做 `CAST` / `TRY_CAST`,输出真正类型化的干净表
-- **ods 是脏数据拦截线**:转换失败的数据不能静默丢弃,必须有可观测的出口(打标记字段、落到 `_err` 分区、或写入专门的数据质量日志表,具体策略 TBD)
-- **ods 不做业务语义加工**:只做"把字符串变回正确类型 + 空值兜底 + 去重",不做字段合并、维度关联、指标计算等 dwd 才做的事
-
-### 8.3 其他框架字段
-
-raw 层是否需要 `etl_load_time` / `src_file` / `src_row_no` 等框架字段,暂不做统一要求,后续实际接入第一批表时再根据需要补充到本节。
-
-### 8.4 ods 层类型映射参考
-
-**总则**:raw 层一律 STRING 兜底同步;类型化在 ods 层完成。下表为 ods 层 `CAST` 目标类型的参考表,具体字段可按业务需要微调(如小金额字段可下沉到 `DECIMAL(16,2)`)。
-
-#### 8.4.1 PostgreSQL → Hive
-
-| PG 类型 | Hive 类型 |
-|---------|-----------|
-| `int2` / `smallint` | `BIGINT` |
-| `int4` / `integer` / `int` | `BIGINT` |
-| `int8` / `bigint` | `BIGINT` |
-| `serial` | `BIGINT` |
-| `bigserial` | `BIGINT` |
-| `numeric` / `decimal` | `DECIMAL(20,4)` |
-| `real` / `float4` | `DECIMAL(20,4)` |
-| `float8` / `double precision` | `DECIMAL(20,4)` |
-| `char` / `character` | `STRING` |
-| `varchar` / `character varying` | `STRING` |
-| `text` | `STRING` |
-| `timestamp` / `timestamp without time zone` | `STRING` |
-| `timestamptz` | `STRING` |
-| `date` | `STRING` |
-| `time` / `timetz` | `STRING` |
-| `boolean` / `bool` | `TINYINT` |
-| `uuid` | `STRING` |
-| `interval` | `STRING` |
-| `tsvector` | `STRING` |
-| `array` | `STRING`(保留 JSON/文本形态,dwd 按需解析) |
-| `hstore` | `MAP<STRING,STRING>` |
-
-**说明**:
-
-- 整数统一 `BIGINT`:避免上游扩位(`int4` → `int8`)时下游被动改表
-- 小数统一 `DECIMAL(20,4)`:覆盖绝大多数金额/比率场景;特殊精度需求(如高精度科学计算)单独评估
-- 布尔用 `TINYINT`(0/1):Hive 的 `BOOLEAN` 与 ORC/Spark 生态兼容性没有 TINYINT 稳定
-- 时间类型全部 `STRING`:保留源端字面量,dwd 层再按需 `to_timestamp` / `to_date`
-
-#### 8.4.2 Elasticsearch → Hive
-
-(待补,首批 ES 埋点库接入时落地)
-
 ## 9. 相关文档
 
 - [命名规范](40-命名规范.md) — 表/字段命名五段式

+ 4 - 2
kb/21-时间语义.md

@@ -90,9 +90,11 @@ DS 项目级 globalParams(poyee-data-warehouse 项目,所有工作流继承
 
 ## 6. dwd / dws / ads 层 dt 语义
 
-待后续设计落地后补充
+dwd / dws 引入**真业务时间**作 dt 锚点(≠ ods 的 update_time 锚点),跨层会出现 dt 错位——同一笔订单在 ods 按 update_time 落分区、在 dwd 按业务时间落分区,两者通常不同
 
-设计预期:dwd 引入真业务时间(订单 create_time / 支付 payment_success_time / 交易 trade_amount 时间等),dwd dt 锚点 ≠ ods dt(update_time 锚点),跨层会出现 dt 错位——同一笔订单在 ods 中按 update_time 进入 dt 分区,在 dwd 中按 create_time 进入 dt 分区,两者通常不同。
+- **dwd**:dt = 业务事件时间(如订单支付 `DATE(payment_success_time)`);每天滚动重算最近 30 天(见 `93-架构决策.md` ADR-09)
+- **dws**:dt = 对齐上游 dwd 业务时间;滚动窗与 dwd 一致(见 `25-dws建模.md` §1.4)
+- **ads**:待建
 
 ## 7. 补数(COMPLEMENT_DATA)语义
 

+ 39 - 1
kb/22-raw建模.md

@@ -1,4 +1,42 @@
-# raw 层建模
+# raw / ods 建模
+
+> 本文档:raw / ods 两层的通用约定(层职责、字段类型、脏数据边界)+ 各源表 raw 层字段目录。
+> 建模方法论见 `20-数仓分层与建模.md`;命名见 `40-命名规范.md`;时间 / 分区语义见 `21-时间语义.md`。
+
+## 0. 通用约定
+
+### 0.1 raw 层:schema-on-read landing
+
+> schema-on-read = 数据写入时不解释类型,读取时再按需解析。这是小数仓 / 列存 / 数据湖共用的范式,与传统 RDBMS 的 schema-on-write 形成对照。
+
+**全 STRING 的设计理由:**
+
+1. **隔离源端类型变化**:源系统改字段类型(`int4` → `bigint`、`varchar` → `text`)raw 入库零影响;类型解释下移 ods,源端变化只动 ods CAST 表。raw 表 schema 长期稳定,避免反复 ALTER 触发 metastore 高频变更
+2. **同步阶段不可失败**:raw 是回源链路兜底层,类型转换中单条脏数据可能让任务整个失败(如某行 `int4` 字段含 `'N/A'` → CAST 异常);STRING 永不失败,全量入仓后再到 ods 拦截脏数据
+3. **保留原始精度与原文**:CAST 可能丢精度(`NUMERIC(38,18)` → `DECIMAL(20,4)` 截断)、丢时区(`timestamptz` 反序列化)、改格式(日期字符串 `'20260101'` 反序列化后再格式化可能不一致);STRING 原汁原味,ods 想怎么解析都行
+4. **脏数据可观测**:业务库历史脏数据(`'1900-00-00'` 日期、`'-1'` 状态、超长字段)必须先入仓再观测;类型化阶段静默丢弃 / 报错跳过就再也看不到。STRING 100% 入仓 + ods 显式标记 / 分流(见 §0.2)
+5. **schema-on-read 范式契合**:小数仓 / 列存(ORC)读取时类型解析成本极低,所以 raw 做 schema-on-read landing + ods 做 schema-on-write 是标准分工;vs 传统 OLTP 的 schema-on-write 必须入库时定型,灵活性差
+
+**何时可破例**:上述理由是工程权衡,不是教条。某个 raw 表场景如果这 5 条 ≥3 条不适用,可单独评估破例方式(如:数据源本就是 self-describing 的 NDJSON / Parquet / Avro 时保留单列裸文本 / 自带类型直存;schema 由上游严控且无脏数据风险时直接 typed 列入仓)。破例必须在 PR / 设计稿明示理由,不是默认。
+
+**规则:**
+
+- **全字段 STRING**:raw 层所有表业务字段以及 `dt` 分区字段一律 `STRING` 类型
+- **同步任务不做类型转换**:DataX ini 里不写 `columnType` 的类型映射(或统一填 `string`),CSV 导入时 SparkSQL 读取后也不 `CAST`
+- **外部表兜底**:raw 层建表一律用 `CREATE EXTERNAL TABLE`,DROP TABLE 只删元数据,HDFS 数据保留;raw 作为链路兜底层,误删元数据时数据仍可 `MSCK REPAIR` / 重建元数据恢复,无需回源库重同步
+
+### 0.2 ods 层:类型转换与脏数据识别
+
+- **ods 是类型化的第一层**:从 raw 的 STRING 字段做 `CAST` / `TRY_CAST`,输出真正类型化的干净表
+- **脏数据边界(术语统一)**:
+  - **ODS 拦截「类型错误」**:CAST 失败的数据(如 `int` 字段含 `'N/A'`),不静默丢弃,必须有可观测出口(标记字段 / `_err` 分区 / 数据质量日志,策略 TBD)
+  - **「值脏」归一不在 ODS**:类型正确但值不规范(如同一概念多种写法 `mlb` / `MLB`)的归一清洗放 **DIM 层**(见 `24-dim建模.md` §1.4)
+- **ods 不做业务语义加工**:只做"把字符串变回正确类型 + 空值兜底 + 去重",不做字段合并、维度关联、指标计算等 dwd 才做的事
+- **类型映射**:PG → Hive 映射规则见 `93-架构决策.md` ADR-06 + `conf/pg-to-hive-type.ini`(单一真值,本文不复制)
+
+### 0.3 其他框架字段
+
+raw 层是否需要 `etl_load_time` / `src_file` / `src_row_no` 等框架字段,暂不做统一要求,后续实际接入时再按需补充。
 
 ## 1. card_group_order_info(拼团订单)
 

+ 2 - 3
kb/23-dwd建模.md

@@ -8,7 +8,7 @@
 
 ### 1.1 框架字段
 
-所有 DWD 表必带 `etl_time TIMESTAMP` + 分区 `dt STRING`,`STORED AS ORC`
+见 `20-数仓分层与建模.md` §5.2 加工层框架字段
 
 ### 1.2 业务过程拆分
 
@@ -21,8 +21,7 @@ DWD 直引 DIM 层已清洗字段,**不在 DWD 二次清洗、不做空值兜
 ### 1.4 分区与写入
 
 - 分区锚点:业务时间(事件发生日,如 `payment_success_time`),不是抽取日
-- 写入策略:**回算近 2 日**(`dt IN (${dt}, ${pdt})`),兜底跨零点 ODS 漂移(业务时间 T-1 但 update_time 漂到 T 的事件,第二天跑批时通过扫 ods.dt=T 兜回);动态分区写入(kb/21 §8 项目默认 DYNAMIC mode 只覆盖 SELECT 出现的 dt,不动其他历史分区)
-- 回算窗口 N:本项目按业界主流 N=2(阿里 OneData / 字节 / 美团默认);金融 / 强稳定性场景可调到 N=3 或更大(kb/20 §7.3 通用兜底默认)
+- 写入策略:**每天滚动重算最近 N=30 天**(`start_date=today-30`,宽扫窄落),覆盖退款窗全程;动态分区写入(kb/21 §8 项目默认 DYNAMIC mode 只覆盖 SELECT 出现的 dt,不动其他历史分区)。回算窗 N 的依据与演进(N=2 → 滚动 30、目标态支付/退款分离)见 `93-架构决策.md` ADR-09;补数任务流见 ADR-11
 - 重跑幂等:`INSERT OVERWRITE PARTITION (dt)` 动态分区
 - 调度依赖:DS DEPENDENT 同 dt ODS + 同 dt DIM ful_d 双前置(按 Kimball 维度退化原则,DWD 必依赖 DIM 同分区跑完)
 

+ 2 - 2
kb/24-dim建模.md

@@ -8,7 +8,7 @@
 
 ### 1.1 框架字段
 
-所有 DIM 表必带 `etl_time TIMESTAMP` + 分区 `dt STRING`,`STORED AS ORC`
+见 `20-数仓分层与建模.md` §5.2 加工层框架字段
 
 ### 1.2 选型策略
 
@@ -36,7 +36,7 @@ ODS 跨 dt 不去重 → 同 pk 多分区并存 → DIM 取每个 pk 的最新
 
 ### 1.4 脏数据清洗位置
 
-源端字段值脏数据(如同一概念多种写法 `mlb` / `MLB`)的归一清洗放 **DIM 层**完成;DWD 维度退化时直引 DIM 已清洗字段,下游 DWS / TDM 不再清洗。
+源端**值脏数据**(类型正确但值不规范,如同一概念多种写法 `mlb` / `MLB`)的归一清洗放 **DIM 层**完成;区别于 ODS 拦截的**类型错误**(CAST 失败,见 `22-raw建模.md` §0.2)。DWD 维度退化时直引 DIM 已清洗字段,下游 DWS / TDM 不再清洗。
 
 ---
 

+ 2 - 2
kb/25-dws建模.md

@@ -8,7 +8,7 @@
 
 ### 1.1 框架字段
 
-所有 DWS 表必带 `etl_time TIMESTAMP` + 分区 `dt STRING`,`STORED AS ORC`
+见 `20-数仓分层与建模.md` §5.2 加工层框架字段
 
 ### 1.2 单一职责:日聚合
 
@@ -36,7 +36,7 @@ DWS 只做 `_1d` 日聚合主题宽表,不爆窗口表(不建 `_30d` / `_y{
 ### 1.4 分区与写入
 
 - 分区锚点:业务时间(与上游 DWD 对齐,如 `DATE(payment_success_time)`)
-- 写入策略:**回算近 2 日**(`dt IN (${dt}, ${pdt})`),与 DWD N=2 对齐(DWD 漂移连锁补偿,参 `93-架构决策.md` ADR-09)
+- 写入策略:**滚动重算最近 30 天**,与 DWD 对齐(DWD 已改滚动 N=30,见 `93-架构决策.md` ADR-09;DWS 聚合 DWD 须同窗才口径一致)。**注:当前 `jobs/dws` SQL 仍为 N=2,滚动 30 的实现待改(见 `90-演进路线.md`)**
 - 分区类型:动态分区 `PARTITION (dt)`
 - 重跑幂等:`INSERT OVERWRITE PARTITION (dt)` 项目默认 DYNAMIC mode(kb/21 §8)只覆盖 SELECT 出现的 dt,不动其他历史分区
 - 调度依赖:DS DEPENDENT 同 dt DWD 跑完

+ 1 - 1
kb/41-开发规范.md

@@ -399,7 +399,7 @@ raw 层的 `jobs/` 有两类主要任务,根据源数据形态选择:
 | 从 MongoDB / PG / MySQL 等结构化源库同步 | `.ini`(DataX 配置) | `bin/datax-single-job-starter.sh` |
 | 从本地 / 外部 CSV 文件导入 | `.sql`(含 `USING csv` 临时视图 + `INSERT OVERWRITE`) | `bin/csv-to-hdfs-starter.py`(阶段 1 实现) |
 
-**raw 层数据类型约定**:全字段 `STRING`,类型转换与脏数据识别下推到 ods 层。契约详见 `20-数仓分层与建模.md` §8.1。
+**raw 层数据类型约定**:全字段 `STRING`,类型转换与脏数据识别下推到 ods 层。契约详见 `22-raw建模.md` §0.1。
 
 **DataX ini 引用数据源约定**:sync ini 里 `[reader]` / `[writer]` 的 `dataSource` 字段必须写成 `{db_type}/{env}-{实例简称}`(例如 `postgresql/prod-hobby`、`hdfs/prod-ha`),指向项目同级目录 `datasource/{db_type}/{env}-{实例简称}.ini`。裸名(如 `hobby`)无法解析。代码按 `/` 切首段取 db_type(即父目录名),实现在 `dw_base/datax/plugins/plugin.py:37`、`plugin_factory.py:34`。跨环境同步(如 test 业务库 → prod HDFS)是常态,不设全局 env 概念,每个 sync ini 显式指向各自 env 的 source ini。
 

+ 1 - 1
manual/ddl/dim/trd/dim_trd_card_group_ful_d_create.sql

@@ -5,7 +5,7 @@
 -- 状态:[草案]
 -- 备注:单源 ods.ods_trd_card_group_info_inc_d;sport→category 数据脏点清洗在 DIM 内置(kb/24 §3.2);
 --      默认 ful_d,未来出"看历史属性变化"标签需求或全量快照存储 > 100GB 时迁 zip_d 双轨共存(kb/24 §1.2);
---      字段类型对齐 ods(整数全 BIGINT,详见 kb/20 §8.4.1 + ADR-06);`lock` 是 SQL 关键字加反引号
+--      字段类型对齐 ods(整数全 BIGINT,详见 ADR-06);`lock` 是 SQL 关键字加反引号
 
 DROP TABLE IF EXISTS dim.dim_trd_card_group_ful_d;
 

+ 1 - 1
manual/ddl/dim/usr/dim_usr_user_ful_d_create.sql

@@ -5,7 +5,7 @@
 -- 状态:[草案]
 -- 备注:base_user LEFT JOIN cert_info 合一;性别/生日仅取 cert(kb/24 §2.2);
 --       默认 ful_d,未来出"看历史属性变化"标签需求或全量快照存储 > 100GB 时迁 zip_d 双轨共存(kb/24 §1.2);
---       字段类型对齐 ods(整数全 BIGINT,详见 kb/20 §8.4.1
+--       字段类型对齐 ods(整数全 BIGINT,详见 ADR-06
 
 DROP TABLE IF EXISTS dim.dim_usr_user_ful_d;
 

+ 1 - 1
manual/ddl/dwd/trd/dwd_trd_order_pay_apd_d_create.sql

@@ -7,7 +7,7 @@
 --      支付成功判定:order_type='group' AND status IN (101,103,104,105,106,301,302)(kb/23 §2.1);
 --      11 字段金额按 mer_act% 通式派生 + Net Revenue 特例(kb/23 §2.5);
 --      维度退化:category/manufacturer/sets/year/group_name/list_id/panini_list_id 来自 dim_trd_card_group_ful_d;
---      字段类型对齐 ods(整数全 BIGINT,详见 kb/20 §8.4.1 + ADR-06);
+--      字段类型对齐 ods(整数全 BIGINT,详见 ADR-06);
 --      _apd_d 事件不可变,只装支付那一刻的事实快照(状态字段 status/serve_status 等归 dim_trd_order_zip_d 拉链)
 
 DROP TABLE IF EXISTS dwd.dwd_trd_order_pay_apd_d;

+ 1 - 1
manual/ddl/dws/usr/dws_usr_user_trade_1d_create.sql

@@ -8,7 +8,7 @@
 --      dt 锚点 = DATE(payment_success_time) 业务时间分区,承袭 dwd(kb/25 §2.4);
 --      回算 N=2 与 dwd 对齐(漂移连锁补偿,kb/93 ADR-09);
 --      不冗余维度退化字段(1 期 scope 服务标签计算,kb/25 §1.3 触发条件再扩);
---      字段类型对齐 dwd(整数 BIGINT,金额 DECIMAL(20,4),kb/20 §8.4.1)
+--      字段类型对齐 dwd(整数 BIGINT,金额 DECIMAL(20,4),ADR-06)
 
 DROP TABLE IF EXISTS dws.dws_usr_user_trade_1d;
 

+ 1 - 1
tests/integration/datax/hive_import/ddl/hive_raw.sql

@@ -7,7 +7,7 @@
 --   - 敏感字段 user_cert_data / cert_no / cert_real_name 不同步(公司规范"敏感字段不出业务库")
 --   - cert_birthday 本轮原样同步未脱敏;正式 raw 库上线前补"脱敏到月"
 --     (方案见 kb/90 §2.6 后延 ADR 高优先级项:DataX 字段级变换)
--- 分层规范:raw 层 EXTERNAL + 全字段 STRING + dt 分区,见 kb/20 §7 / §8.1
+-- 分层规范:raw 层 EXTERNAL + 全字段 STRING + dt 分区,见 kb/20 §7 / kb/22 §0.1
 
 CREATE EXTERNAL TABLE IF NOT EXISTS test.raw_usr_app_user_cert_info_inc_d (
     id            STRING COMMENT 'id',