原则是用来坚守的,原则是会被打破的,每次妥协前知晓代价,代价可承担就好。坚守不住的时候想想为什么数仓是一把手项目。
做数仓的人都有过这种经历:
接手一张"祖传"的表,字段200+,没人说得清粒度是什么;跑出来的数和业务方对不上,排查三天发现是两个口径各算各的;好不容易上线的模型,三个月后因为新需求要推倒重来。
这些问题的根源不是SQL写得不好,是建模的时候没有原则,或者有原则但没守住。
以下是我这些年踩坑之后沉淀下来的建模原则。不是教科书上的理论,是拿血泪教训换来的。
一张事实表中的每一行必须代表同一层级的业务事件。不同粒度(如订单级和订单明细级)绝不混在同一张表中。混粒度会导致度量值无法直接聚合,上层查询必须带隐含过滤条件才能得到正确结果。
首先思考事实表的主键是什么,如果主键是联合主键,且组成联合主键的非ID字段比较多,则需慎之又慎
建表的第一步不是列字段清单,而是用一句话回答:"这张表里一行代表什么?"
这个问题答不清楚,后面所有字段选择都是在赌运气。
粒度一旦声明,哪些维度该放进来、哪些度量有意义就自然确定了。
"活跃用户"这个口径,在你的数仓里有几个版本?如果答案大于1,恭喜,你已经埋下了一颗定时炸弹。
同一个概念只应有一个权威计算位置。下游所有消费方引用结果,而不是各自重新实现。每多一个副本,就多一个将来对不上的隐患。
用户维度、时间维度、地区维度——这些跨业务共用的维度必须统一维护。两个事实表如果用的不是同一份用户维度,你在SQL里JOIN它们就是在做无意义的笛卡尔积。
ADS直接读ODS,DWS跳过DWD去读源表——每次看到这种SQL我就知道,这个团队迟早要出事。
穿透访问意味着绕过了清洗和标准化逻辑。源头改了一个字段类型,你的ADS毫无感知地产出了一堆脏数据。
同一段逻辑出现在三个以上的地方?沉淀为公共层。但也别过度下沉——公共层每多一张表,改动成本就多一分。这是个平衡的艺术。
每一次冗余都要回答三个问题:
答不上来,就别冗余。
判断标准很简单:这个字段的更新频率和生命周期,跟表里其他字段一致吗?
用户基本信息一年不变一次,用户最近7天活跃天数每天都在变——它们不该在同一张表里。
宽表是结果,不是目标,不是工具
event_time、create_time、etl_time——这三个东西含义完全不同。但我见过无数张表,就一个 dt 字段,没人说得清它到底代表什么。
每个时间字段必须声明:它是事件发生时间,还是入库时间,还是处理时间?顺便把时区也定了。
用户等级会变,商品分类会调,组织架构会重组。"暂时不考虑变化"等于默认选了Type 1(直接覆盖),而且在出问题之前没人会发现这个决定。
建表时就得为每个字段声明:变了怎么办?覆盖?拉链?还是加新列?
"大家都知道这张表的主键是order_id"——这句话翻译一下就是"这张表的主键没有任何校验,重复了也没人知道"。
没有显式主键的表无法安全JOIN,无法判断重复,无法做幂等写入。
明细层SUM出来的金额,必须等于汇总层的数字。拆出去的多张表,金额之和必须等于源头总数。
无法勾稽的模型,出了问题就是一笔糊涂账。
NULL是"不知道",0是"确认为零",空字符串是"确认为空"。三者不能混用。随意用默认值填充NULL,是在掩盖问题。
今天跑和明天跑,任务跑几遍,同样的输入必须产出同样的结果(数据幂等)。这意味着:
数据一定会出错,这不是如果,是什么时候的问题。
某天的数据写坏了,能不能只重跑那一天?维度表历史记录要修正,影响范围有多大?让纠错代价可控的模型,才是能在生产环境活下来的模型。
每张表都在烧存储、烧计算、烧维护人力。建表时就该定义:保多久?刷多频繁?没人用了多久之后可以下线?
不管生命周期的仓库,只会长大,不会变好。
分区字段代表数据的物理隔离边界,决定了查询扫描范围和生命周期管理的最小单位。分区策略必须与业务数据的产生节奏一致——按天产生的数据按天分区,按小时到达的数据按小时分区。错位的分区会导致要么多扫数据,要么跨分区才能拿到完整快照。
dws_trade_order_1d 这个名字承诺了:汇总层、交易域、订单粒度、天级聚合。
任何人看到表名就能判断这张表能不能满足自己的需求。如果看了名字还得打开文档甚至读SQL才能确认——命名就失败了。
每个指标必须能落地为:表 + 字段 + 过滤条件 + 聚合方式。
如果一个指标定义写不成确定的SQL,它还停留在需求阶段。口径文档脱离模型独立维护,三个月后一定和实际产出对不上。
"1=有效 2=无效"只写在代码注释里?等着吧,迟早有人把2当成"待审核"用。
核心层的模型一旦上线并被下游依赖,其结构变更的成本远高于最初设计时多想一步。宁可在设计阶段多花时间讨论粒度和维度边界,也不要追求快速上线后再重构。
循环依赖 = 调度无法确定顺序 = 一个节点挂了全链路不可控。
如果发现两张表互相依赖,说明职责划分出了问题。
用户信息来自APP注册和客服系统,两个源的手机号不一致,以谁为准?
不定义归一规则就整合,得到的不是完整视图,是一坨混合数据。
这些原则不复杂,甚至有些显而易见。但建模的难不在于知道这些原则,而在于在项目压力下、在需求催促中、在"先上线再优化"的诱惑面前,还能守住它们。
每一条被违反的原则,都会变成未来某天的一次紧急排查、一次数据事故、一次推倒重来。
建模的时候多花一天想清楚,等于给未来的自己省三个月。