瀏覽代碼

docs(kb): 20 §8.1 补 raw 全 STRING 的设计理由 + 破例口子

5 条核心理由(隔离源端类型变化 / 同步不可失败 / 保留原始精度 /
脏数据可观测 / schema-on-read 范式契合)+ 破例判定(5 条 ≥3 不适用
按需评估,PR 说明理由)
tianyu.chu 1 周之前
父節點
當前提交
e1c99b8367
共有 1 個文件被更改,包括 14 次插入0 次删除
  1. 14 0
      kb/20-数仓分层与建模.md

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

@@ -349,6 +349,20 @@ RDS PG / ES ──DataX──▶ RAW ──SparkSQL──▶ ODS ──▶ DWD 
 
 ### 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` / 重建元数据恢复,无需回源库重同步