
简介《数仓建设规范指南.pdf》是一份面向数据仓库开发、建模与运维人员的规范化参考手册聚焦解决数仓分层混乱、开发口径不一致、公共规范缺失等常见问题。内容分为数据模型架构原则、数仓公共开发规范、各层开发规范与命名规范四大章节详细讲解了分层原则、主题域划分、数据模型设计、层次调用、数据类型、冗余管理、NULL 字段处理、指标口径、表生命周期并在预览中补充了词根设计、表命名和指标命名等细则。资源为单个 PDF 文件整包 920KB约 22 页目录结构清晰既可作为企业数仓建设落地的制度模板也能用于日常开发评审与培训材料。已有 984 人在线学习适合数据平台工程师、数仓建模初学者以及需要统一团队标准的技术管理者阅读。1. 数仓分层不是越多越好先定规矩再谈建模一个数据团队在数仓建设上的血泪教训通常是第一步不是买组件也不是画架构图而是先回答“我到底需要几层”。三层够用五层美观但每多一层调度链路就多一跳排查问题就多一道迷宫。真正有效的做法是用一套规范把分层边界、字段类型、NULL 规则、生命周期、命名词根全部钉死再让工具去强制检查。《数仓建设规范指南》这份文档正好覆盖了数据模型架构原则、公共开发规范、各层开发规范、命名规范四块内容属于能直接抄作业的团队公约。适合正在从零搭数仓的中小型团队也适合已经被表名看不懂、指标口径打架、存储只涨不降折磨的维护者。下面按拆文档的顺序把能落地的部分逐层讲透。2. 分层架构与主题域ODS/DWD/DWS/APP 职责边界与建模原则2.1 ODS 到 APP 各层职责哪些活该在哪层干ODS 层最接近数据源这一层只做原样接入不做数据清洗。规范的原话是“不建议做过多的数据清洗工作”原因是后续追溯数据问题时需要看到源系统的原始面貌。去噪、去重、异常值处理这些动作全部下沉到 DWD。DWD 层要处理的脏数据包括状态定义不一致、命名不规范、垃圾数据同时做维度退化把高频使用的维度直接合并进事实表减少下游 join 成本。DWS 层做公共汇总粒度比明细粗围绕主题域产出宽表目标是覆盖 80% 的应用场景。APP 层最薄是给数据产品和 BI 用的出口数据可以落在 ES、Redis、PostgreSQL、Druid 甚至 Hive 里。层次职责边界典型表/产物是否允许直连源系统ODS原样同步源数据保留追溯能力订单流水副本、日志原始表是DWD清洗整合、维度退化、规范化订单明细事实表、用户明细否DWM轻度聚合产出公共中间表商品日汇总中间表否DWS公共汇总产出宽表商品宽表、用户宽表否APP面向应用的数据出口报表数据集、接口数据否这套分层的好处可以归纳成五条清晰数据结构、数据血缘追踪、减少重复开发、数据关系条理化、屏蔽原始数据的影响。拆开看前两条是治理收益后三条是效率收益。血缘能追踪的前提是每层只依赖紧邻的下一层如果 ODS 被 DWS 直连、DWD 依赖了 APP血缘图就会变成乱麻任何任务报错都很难定位影响面。另一个容易被忽略的点是DWS 表不能跨主题域不能把流量域和交易域的字段全塞进一张表表面上看着省事实际上让这张表的依赖方变成全公司任何字段变更都会大面积受影响。2.2 DWM 要不要保留两套数据流向的取舍DWM 层在原文档里的地位很微妙先定义它是在 DWD 基础上做轻度聚合、生成公共中间表随即又明确说“由于宽和窄的界限不易界定也可以去掉 DWM 这一层”。这个判断很务实。DWM 存在的唯一理由是复用同一份聚合结果被多个 DWS 表引用把它沉淀下来就是合理的如果每个 DWM 表只服务一张 DWS 表它就是在增加调度依赖和存储成本。稳定业务的数据流向是 ODS - DWD - DWS - APP。非稳定业务或探索性需求可以走 ODS - DWD - APP 或 ODS - DWD - DWM - APP。三条链路不是并列关系而是按业务稳定程度分级越稳定的业务越走完整链路越探索性的需求越走短链路。短链路让江湖救急的速度更快长链路保证质量和口径统一。判断要不要保留 DWM拿三个信号对照同一份轻度聚合是否被 3 个以上下游复用直接由 DWD 加工到 DWS 是否超过两小时有没有不同主题域共享同一粒度对账数据的需求。三个信号全部否定就把 DWM 删掉把 DWS 直接建立在 DWD 之上。提示DWM 层一旦决定保留就不要再出现 DWD 直连 DWS 的旁路否则 DWM 沉淀出的公共表没人用调度链路还白多一跳。2.3 主题域划分按业务过程抽象而不是按部门堆表主题域划分容易被业务部门带着走订单域、用户域、商品域听着合理边界却模糊。规范给出的思路是先识别业务过程再划分数据域。下单、支付、退款是三个业务过程它们都属于交易域买家下单事件里买家是维度下单是过程。一个业务过程不可拆分是定义指标的最小场景。数据域是对业务过程和维度的抽象集合长期维护不轻易变动新业务进来要么被已有数据域包含要么扩展新域。实际操作中我一般会用一张二维表来收敛纵轴是数据域横轴是业务过程单元格里填对应的指标。如果出现一个指标同时落在两个单元格说明业务过程没有切干净需要回头调整数据域边界。这种收敛应该由数仓团队主导业务方只负责确认指标口径不能按部门墙来定义域。2.4 数据模型设计高内聚低耦合与公共逻辑下沉模型设计四条原则高内聚低耦合、核心模型与扩展模型分离、公共处理逻辑下沉、成本与性能平衡。前两条决定模型怎么切后两条决定字段和逻辑怎么放。公共逻辑下沉可以用建表约束来体现-- 公共逻辑下沉示例DWD 层统一完成脱敏和标准化 -- 手机号、身份证号在 DWD 一次性清洗APP 层禁止重复实现 CREATE TABLE dwd_user_info_di ( user_id BIGINT COMMENT 用户ID, phone STRING COMMENT 脱敏手机号已隐去中间四位, id_card STRING COMMENT 脱敏身份证号, user_level STRING COMMENT 用户等级S/A/B/C, etl_time STRING COMMENT ETL处理时间 ) COMMENT 用户维度明细公共清洗在DWD完成 PARTITIONED BY (dt STRING COMMENT 统计日期);这段 SQL 的关键点是脱敏逻辑只有 DWD 这一份实现。如果下游报表需要完整手机号不应该自己在 APP 层写一遍替换逻辑而是回 DWD 扩充字段把“脱敏后明细”和“加密后原始值”分两个字段暴露。这就是公共处理逻辑下沉的工程含义同一份逻辑只允许出现一次变更也只改一处。成本与性能平衡这条容易走偏。适当冗余可以换查询刷新性能但冗余字段要有明确门槛下游 3 个及以上使用方、不造成过多延后、与已有字段重复率不超过 60%。3 个使用方是为了保证冗余收益大于维护成本不延后是为了避免为单个字段拖慢整个宽表刷新重复率 60% 是为了防止用冗余替代建模。3. 公共开发规范工程化调用链、数据类型、NULL 与表分类3.1 层次调用规范用 SQL 扫描反向依赖规范如果不带检查手段三个月后就只剩纸面文章。层次调用是最容易被违反的规范因为开发者在赶进度时总有理由“直连 ODS 更快”。调用链的约束有六条正常流向 ODS - DWD - DWM - DWS - APP禁止反向依赖比如 DWM 不能依赖 DWSDWM、DWS、APP 禁止直接引用 ODS 表ODS 只能被 DWD 消费同一主题域内避免 DWM 再生成 DWM出现 ODS - DWD - DWS 说明主题域覆盖不全DWD 应落到 DWM使用频度极低的表允许 DWD - DWS但要登记原因。把每条规则映射到血缘数据上就是一张可以定期扫描的 SQL-- 扫描调度血缘中的非法依赖 -- lineage: 血缘表至少包含 src_table/dst_table 两个字段 SELECT task_id, src_layer, dst_layer, COUNT(*) AS violation_cnt FROM lineage WHERE (dst_layer DWS AND src_layer IN (ODS, DWD)) OR (dst_layer DWM AND src_layer IN (ODS, DWS)) OR (dst_layer DWD AND src_layer IN (DWM, DWS, APP)) GROUP BY task_id, src_layer, dst_layer;这个查询把违规依赖分成三类目标是 DWS 但源是 ODS/DWD说明主题域不完整目标是 DWM 但源越级目标是 DWD 但源在上游层属于反向依赖。task_id是调度任务标识用来定位具体哪个 Job 违规。查到结果后不需要手工催把这条 SQL 挂到调度平台的自检任务里每天跑一次违规任务自动发告警到负责人。没有血缘表的团队可以退一步抓调度平台 DAG 配置按表名前缀推断层级效果略差但够用。提示反依赖查一次不难难的是新任务上线时也执行同样的检查。把这条 SQL 做成调度任务的前置校验不通过就不允许提交调度。3.2 数据类型统一金额、ID、时间的选型逻辑数据类型不一致是口径打架的第一来源。同一个金额字段A 表用 doubleB 表用 decimal(28,6)关联结果就会在精度上出现偏差。规范给出的选型非常干脆字段类别统一类型原因金额double 或 decimal(28,6)精度可控必须明确单位是分还是元字符串stringHive/Spark 下统一 string避免 char/varchar 混用ID 类bigint数值 ID 不用字符串避免隐式转换时间string避免 timestamp 时区问题按字符串排序即按时间排序状态string枚举值统一编码禁止数字和中文混用这套选型的核心逻辑是用最简单的类型表达最确定的语义。时间用 string 而不是 timestamp在 Hive 场景下能规避时区转换引发的数据错位金额用 decimal(28,6) 而不是 double能避免浮点加乘后出现 1.9999 这类尾巴。单位问题更隐蔽有的订单表金额单位是分有的是元两个表 join 时差了 100 倍。规范要求“明确单位是分还是元”我一般会在字段注释里强制带上单位后缀比如order_amt_yuan、order_amt_cent从命名层面消灭歧义。3.3 NULL 字段处理与数据冗余易被忽略的细节NULL 处理规范只有两句话维度字段 NULL 填 -1指标字段 NULL 填 0。实现时建议统一用 COALESCE-- NULL 处理规范示例 -- 维度字段: COALESCE(xxx, -1), 指标字段: COALESCE(xxx, 0) SELECT COALESCE(a.user_gender, -1) AS user_gender, -- 维度 COALESCE(b.pay_amount, 0) AS pay_amount, -- 指标 COALESCE(NULLIF(b.order_cnt, 0), 0) AS order_cnt -- 0也当空处理 FROM dwd_user_info_di a LEFT JOIN dwd_trade_order_di b ON a.user_id b.user_id WHERE a.dt 2024-01-01;注意NULLIF(b.order_cnt, 0)把业务上的 0 转成 NULL再由外层 COALESCE 兜底实现“NULL 和 0 统一填 0”的效果。这里有个细节维度字段填 -1 后一定要保证维表里存在 -1 的记录否则 join 时还是查不出描述。填 -1 的本质是给 NULL 一个可识别的业务占位而不是让它凭空出现。冗余规范容易被业务倒逼突破需求来得急直接在现有表上塞 5 个冗余字段。量化标准其实给得很清楚冗余字段需要满足三条件——下游 3 个及以上使用方、不会造成本表数据延迟、与已有字段重复率不超过 60%。三条同时满足才允许加。临时业务只差 1 个字段直接 join别加字段。3.4 增量、全量、快照、拉链表四类表语义与选择表类型数据特征分区策略使用场景增量表只报上次导出后的新数据每天一个分区日志、点击流全量表每天报全部最新状态只有一个分区小配置表快照表按日分区记录截止当日的全量一天一个分区用户状态、账户余额拉链表记录完整生命周期保留最新状态只有一个分区订单状态、会员等级变化选择这四类表的判断顺序数据是否需要回溯历史需要就选快照表或拉链表变化频率高且数据量大快照表成本高改用拉链表变化频率低、数据量小直接全量表。增量表只适合像日志这种基本不会回改的数据一旦上游发生数据修正增量表的下游必须重跑对应分区运维成本立刻显现。4. ODS、维度层与 DWD 开发规范同步策略、一致性与事实表落地4.1 ODS 同步规范一张源表只同步一次ODS 层有两个容易被低估的细节。第一同步频次和分区策略。规范要求一个系统源表只允许同步一次全量初始化同步和增量同步的处理逻辑要清晰按统计日期及时间分区存储。第二目标表字段在源表不存在时要自动填充处理不能在同步任务里默默丢弃字段。ODS 表的生命周期分类型对待ODS 流水全量表不可再生永久保存日志按留存要求ODS 镜像型全量表按天存储历史变化要保留最新数据放最大分区ODS 增量数据有对应全量表时建议只保留 14 天没有则永久保留ETL 过程中产生的 ODS 临时表最多保留 7 天用完即删。数据质量方面全量表必须配置唯一性字段标识分区空数据要有监控枚举类型字段要监控枚举值变化和分布。最有实操价值的是环比监控对 ODS 表的数据量级和记录数做环比如果今天比昨天突然少了 30%大概率是同步链路出问题。把环比做成调度任务前置校验能在源端故障时让数仓最早感知。4.2 公共维度层字段一致性与访问跨度保留策略公共维度层的准则是“一致性、组合与拆分”。一致性指同一维度在不同物理表中的字段名称、数据类型、数据内容必须一致历史不一致的要做版本控制。组合原则强调把天然关联、经常一起查询的维度放在一起比如商品基本属性和所属品牌无相关性的杂项维度可以合并成集合维度行为维度比如点击量分桶 0-1000、1000-10000这类经过度量计算的字段下游当维度使用也可以放进来。拆分则是按重要性、业务相关性、源、使用频率拆成核心表和扩展表避免扩展字段污染核心模型。维表保留策略有一个量化规则按 3 个月内最大访问跨度来定3个月最大访问跨度建议保留分区数 4 天最近 7 天 12 天最近 15 天 30 天最近 33 天 90 天最近 120 天 180 天最近 240 天 300 天最近 400 天这个阶梯背后的逻辑是保留“最大访问跨度 安全冗余”。只有 4 天访问跨度却保留一年分区纯属浪费存储跨度接近 300 天的只留 400 天也基本覆盖回溯需求再长要靠归档系统而不是分区表解决。4.3 DWD 事实表事务型、周期快照与累积快照DWD 层核心产物是事实表规范把事实表分成三类。第一事务型事实表每行代表一个业务事件粒度最细一般用事件发生日期做分区字段。设计原则包括基于某个业务过程构建、维度退化减少 join、冗余子集降低 IO。第二周期快照事实表每行汇总了标准周期内发生的多个度量事件粒度是周期性的不是个体事务通常包含许多事实。第三累积快照事实表面向多个业务过程联合分析比如采购单流转环节用来分析事件时间之间的间隔周期适合处理关闭、发货这类统计。事务型事实表建表约束示例-- 事务型事实表示例交易订单事实 -- 分区字段使用事件发生日期维度退化字段直接内嵌 CREATE TABLE dwd_trade_order_fact_di ( order_id BIGINT COMMENT 订单ID, user_id BIGINT COMMENT 用户ID, item_id BIGINT COMMENT 商品ID, shop_id BIGINT COMMENT 店铺ID, 维度退化, order_amt DECIMAL(28,6) COMMENT 订单金额(元), pay_amt DECIMAL(28,6) COMMENT 实付金额(元), order_status STRING COMMENT 订单状态, pay_time STRING COMMENT 支付时间, etl_time STRING COMMENT ETL处理时间 ) COMMENT 交易订单事务事实表 PARTITIONED BY (dt STRING COMMENT 事件发生日期);这里把 shop_id 直接退化到事实表而非单独做店铺维表 join是因为 90% 的订单查询都按店铺维度过滤退化后显著减少下游 join 次数。order_status 保持字符串枚举而不是数字编码是为了让多维分析工具直接展示可读状态。周期快照表和累积快照表结构类似但分区语义不同周期快照按周期结束日分区累积快照的每个分区代表一个完整过程的结束时间窗口。5. DWS 聚集设计与生命周期管理从宽表到保留策略5.1 聚集四步从明细到宽表的加工路径DWS 是公共汇总层目标是让 80% 的查询不用访问明细。聚集的基本原则是聚集表必须与查询明细粒度数据时的结果保持一致不要在同一个表中存储不同层次的聚集数据聚集粒度可以不同只关心查询需要的维度。第一条最难保证因为明细和汇总跑在不同调度上数据口径稍有变化两边就对不上。经验做法是让聚集表统一由明细表加工杜绝两边各自从 ODS 读取。聚集分四步确定聚集维度、确定一致性上钻、确定聚集事实、命名统计周期。以交易为例只关心商品交易额就按商品维度聚集上钻要明确按天还是按月、按商品还是按类目事实要明确是交易额还是成交数量命名带统计周期后缀。-- DWS 宽表命名与设计示例 -- 商品维度日汇总宽表, _1d 表示最近1天 CREATE TABLE dws_item_sales_1d ( item_id BIGINT COMMENT 商品ID, gmv_amt DECIMAL(28,6) COMMENT 成交总额(元), order_cnt BIGINT COMMENT 成交订单数, buyer_cnt BIGINT COMMENT 成交买家数, refund_amt DECIMAL(28,6) COMMENT 退款金额(元), etl_time STRING COMMENT ETL处理时间 ) COMMENT 商品日汇总宽表 PARTITIONED BY (dt STRING COMMENT 统计日期);字段选型逻辑是先放最核心的成交指标再逐步补充退款、售后这类扩展指标。不要在第一天就把所有指标全砌进宽表扩展字段会拖慢刷新还让表结构难以维护。规范里的“数据公用性”要这么判断这个汇总是否被 3 个以上使用方依赖如果不是它就不该占用 DWS 资源直接在 DWD 或 APP 层处理。5.2 公共汇总层的统计周期符号公共汇总层设计原则里有一条落地最直接不跨数据域在表命名上说明统计周期。_1d表示最近 1 天_td表示截至当天_nd表示最近 N 天。这三个后缀是 DWS 表名的一部分让使用方从表名直接判断数据时效。这里有个常见偏差_1d不等于_td。_1d是最近一个自然日比如今天跑昨天全天数据_td是截至当前时间的累计比如截至今天 12 点的累计成交。一个是闭区间一个是开口区间二者在下游 join 时如果混用报表数字一定对不上。5.3 生命周期管理矩阵按 P0-P3 定保留策略生命周期管理的核心不是“统一保留 365 天”而是按价值分等级。历史数据等级分 P0-P3P0 是非常重要且不可恢复的数据包括交易、日志、集团 KPI、IPO 关联表P1 是重要但不可恢复的业务和应用数据P2 是重要但可恢复的数据比如交易线 ETL 中间过程P3 是不重要且可恢复的数据。配合表类型生命周期矩阵如下表类型推荐保留策略对应等级事件型流水表(增量)按业务留存要求日志类按合规周期P0 或 P1事件型镜像表(增量)镜像历史多版本按天存储P1维表保留最长访问跨度 冗余区间P1Merge 全量表历史保留前一天分区当前分区为主P2ETL 临时表最多保留 7 天P3TT 临时数据默认 93 天可缩短P3普通全量表按历史数据等级确定P2 或 P3这个矩阵的价值是把“留多久”变成一个可计算的决策。数据量大的业务比如日志流水按 P0 永久保存会导致存储成本爆炸所以要叠加一条永久保存可以降级为冷存储归档热库只留近 N 天分区。归档和删除是两个动作归档意味着还能找回删除是彻底释放存储判定基准是数据是否可恢复。6. 词根与表命名用自解释的表名减少沟通成本6.1 词根命名的最小原子词根是数仓命名规范里的最小约定单元。比如货架业务英文名是 rack那所有表、字段涉及货架的地方都统一叫 rack不允许出现 shelf、storage 这类同义词。指标里的“率”统一定义为 rate所有率指标都叫 xxx_rate。词根做表名、字段名、主题域名的统一基准团队越大词根表越要维护得像字典。新成员能不能快速上手写数仓 SQL很大程度上取决于他能不能查词根表。6.2 五类表的命名规则常规表命名分层前缀_部门_业务域_主题域_XXX_更新周期|数据范围。分层前缀和周期符号是固定的前缀/后缀含义dim维度表odsODS 层dwdDWD 层dwsDWS 层dmDM 层/集市层_d日快照_i增量_f全量_w周_l拉链表_a非分区全量表中间表命名是mid_table_name_[0~9|dim]table_name是任务目标表名后面用数字或 dim 区分不同中间结果。临时表统一tmp_前缀只允许测试使用不允许被正式任务依赖。维度表统一dim_前缀。手工表统一放dwd_业务域_manual_xxx下通过 manual 标识说明变更要人工介入。6.3 用 awk 快速扫描命名合规性规范落地最好的方式是让命名检查进入 CI。下面这段 awk 脚本可以用在代码提交或调度发布前# 检查建表SQL中的表名是否符合约定 awk /CREATE TABLE/{print $3} create_table.sql | \ awk { if ($0 ~ /^dwd_/ || $0 ~ /^dws_/ || $0 ~ /^ods_/ || $0 ~ /^dim_/ || $0 ~ /^tmp_/ || $0 ~ /^mid_/) { split($0, arr, _); if (arr[1] tmp $0 !~ /^tmp_/) print 非法临时表: $0; if (arr[1] dwd NF 4) print dwd表缺少业务域前缀: $0; } else { print 未知前缀表: $0 } }脚本做的事很简单从建表 SQL 里抓出CREATE TABLE后面的表名先校验前缀是否在白名单里再对dwd表校验字段段数是否足够。抛出的错误信息可以直接对接 CI 的注释机器人开发者在合并请求里就能看到是哪张表命名不合规。命名规范如果只写在文档里早晚会被绕过变成 CI 检查后才能成为真正强制的工程约束。本文还有配套的精品资源点击获取