做数据仓库的人,多少都遇到过这种尴尬:同一张订单表,A 团队加工了一遍,B 团队又加工了一遍,C 团队第三遍;同一个“活跃用户”的定义,三个报表能算出三个数,业务方拿着截图来问你到底信谁。数仓越建越乱,不是数据量的问题,也不是技术栈的问题,而是从一开始就没把复用性当成一个核心设计目标。
这篇文章,我结合自己这些年设计和构建数仓的实操经验,把“高复用性数仓”这件事掰开揉碎讲清楚。从架构分层、模型设计、命名规范到实际落地中的坑和排查思路,尽量做到不空谈理论,每一条都能拿回去直接用。
1. 为什么大多数数据仓库越用越难用:复用性差的病根
很多人对数据仓库的理解是“把数据从业务库同步过来,然后写 SQL 出报表”。如果只是这种用法,短期看不出问题,但只要数据源一多、业务线一多、指标一多,问题马上集中爆发。我认为,绝大多数数仓项目失败,不是输在技术上,而是输在从一开始就没把“复用”当一回事。
1.1 烟囱式开发引发的死循环
烟囱式开发是数仓体系里最典型的反面教材。它的表现非常直白:每个业务方提出报表需求时,开发人员直接从 ODS(原始数据层)拉数据,写一大段嵌套查询,算出结果后放到一张独立的报表表里。第一张报表没问题,第二张也还行,等到第三十张报表时,你会发现底层那几张 ODS 表已经被几十个任务引用,每个任务里都有几乎一模一样的长 SQL。
这种模式的问题在于,每一次需求都是“一次性开发”。今天算订单金额,把 sum(amount) 写一遍;明天算退款金额,又把 sum(refund_amount) 写一遍。表面上开发速度很快,实际上是在不断积累技术债。员工一旦离职,后人完全看不懂那些长达几百行的 SQL 在算什么,只能推倒重来。烟囱式开发走到后期,数据血缘已经是一团乱麻,没人敢动底层表结构,因为不知道会影响哪些下游。
1.2 复用性差的真实代价
复用性差不只是“不好看”、“不规范”这种主观感受,它的代价是可以量化的。我见过一个中型互联网公司,数仓团队 8 个人,每个月要接 200 多个报表需求,其中至少一半是重复计算。假设每次需求平均需要 2 天开发、1 天测试,那么每月浪费的人力大约在 40 人日左右。一年下来就是 480 人日,相当于 2 个全职开发完全白干。
更隐蔽的代价是数据口径混乱。同一个“GMV”,不同人写出的 SQL 过滤条件不一样,有的去掉了退款单,有的没去掉,有的甚至把测试订单也算进去了。业务部门一旦发现数据对不上,对数据团队的信任就会崩塌。信任这个东西一旦丢了,再想建立起来,比从零开始做一个数仓还要难。
1.3 复用性好的数仓应该长什么样
反过来看,一个高复用性的数仓应该是这样的:底层原始数据只加工一次,形成标准化的中间层;业务方出报表时,直接基于中间层取数,不需要关心底层细节;指标口径是全局统一的,一个指标在哪个报表里查都是同一个结果;新增需求时,80% 的场景可以直接用已有模型完成,剩下的 20% 才需要新的模型开发。
这里有一个数据可以说明问题。阿里内部有个说法叫“烟囱率”,统计的是有多少需求是直接基于 ODS 层开发的。烟囱率越低,说明复用性做得越好。按我的经验,健康状态下的烟囱率应该控制在 10% 以下。也就是说,100 个需求里最多有 10 个是直接基于 ODS 出数,其他 90 个都应该建立在公共层之上。
2. 高复用性数仓的设计框架:先搭骨架再填肉
构建一个高复用性的数仓,最怕的就是“边写边想”。没有整体设计就开始建表,后面大概率会返工。所以我把设计框架放在最前面讲,这是整个项目的地基。
2.1 分层架构中的复用性思想
数仓分层不是新鲜概念,但很多人只记住了 ODS、DWD、DWS、ADS 这几个缩写,没想明白每一层存在的意义。在我看来,分层的核心目的只有一个:控制依赖方向,让每一层只服务于它上面的一层,从而为复用制造条件。
我比较推崇的经典分层结构是这样:
- ODS 贴源层:保留业务库原始数据,不做任何加工,只做清洗、去重、格式统一。
- DWD 明细层:以业务过程为核心,把事实数据和维度数据整合成明细模型,是复用的第一道关口。
- DWS 汇总层:按照业务主题对明细数据做轻度汇总,通常以宽表形式存在,是复用频率最高的一层。
- ADS 应用层:面向具体业务场景的输出层,只做数据组织和呈现,不承担任何复杂加工逻辑。
这里的关键是依赖方向必须是单向的。DWS 只能依赖 DWD,不能跨层依赖 ODS;ADS 只能依赖 DWS,原则上不直接读 DWD 甚至 ODS。如果你发现某张表被封为 DW 层,实际却是直接从 ODS 同步过来的原始数据,那就是分层失守了。
2.2 维度建模是复用性的基石
说到复用性,就必须谈到维度建模。这个理论由 Kimball 提出,到今天依然是大数据领域最主流的数仓建模方法。它的核心思想非常简单:将业务过程抽象为“事实 + 维度”的结构。事实表存度量值,比如订单金额、下单数量;维度表存描述性信息,比如商品名称、用户性别、店铺所在城市。
为什么维度建模对复用性帮助这么大?因为它提供了一套稳定且通用的分析框架。无论业务怎么变,订单这个业务过程的“事实”和“维度”不会轻易变。基于订单事实表加商品、用户、店铺、时间这几个维度,几乎可以回答所有和订单相关的业务问题。
举个例子。先用订单事实表和商品维度表关联,生成一张“商品订单明细表”。业务方想看“每个分类的销售额”,直接按分类聚合;想看“每个品牌的退款率”,也还是用这一张表,加上退款金额字段,按品牌聚合。如果当初没有这张明细表,每个需求来了都要从头关联一遍,那就是典型的复用性失败。
2.3 一致性维度:让不同模型能“对齐”
维度建模虽然好,但很多团队在落地时忽略了一个重要原则:一致性维度。这个词听起来有点抽象,我用大白话解释下。一致性维度指的是,同一个维度在不同的事实表里,必须是同一个维度、同一个键、同一个属性值。
举一个反例。订单表里的“用户”和退款表里的“用户”如果来自两套不同的用户维表,一个用 uid 作为主键,一个用 member_id 作为主键,那么这两张表永远无法关联。业务问“退款用户里有多少是当天的新增用户”,听起来是个很简单的问题,但因为维度不一致,你要先做一次映射转换,这种映射只要有一次没维护好,数据就对不齐了。
设计一致性维度的做法,是把全公司共享的核心维度(用户、商品、商家、地区、时间等)做成了唯一的、标准化的大维表,放到公共层,任何模型要关联这个维度,都必须用这一套,不允许另起炉灶。这就是复用性的底层保障。
3. 构建高复用性数仓的实操要点:从模型到规范
框架谈完了,接下来是实操。这一部分会涉及很多具体的设计决策,比如怎么建模、怎么拆主题、怎么定指标、怎么起名。每一件事看着都不大,但叠加起来,就决定了数仓好不好用。
3.1 公共模型的建设:一次建模,多处复用
公共层模型是整个数仓复用性的核心载体。我见过一些团队,虽然也分了三层,但 DWD 层建表的原则是“接到需求就建表”,完全不考虑这个模型能不能被其他场景复用。结果是 DWD 层建了上千张表,真正被复用的不超过 20%,其余都是某个需求专用的临时加工产物。
DWD 层的模型建设,应该是以“业务过程”为单位,而不是以“报表需求”为单位。一个业务过程,比如下单、支付、退款、发货,建一张明细事实表。这张表尽可能完整地记录这个业务过程发生的事实,并且挂上所有需要用到的维度外键。后续任何分析需求,只要涉及这个业务过程,都应该从这张表出发。
DWS 层同样要注意复用。它是对 DWD 明细的轻度汇总,可以按“主题 + 粒度”来组织。比如“用户主题”可以按 日、周、月三种粒度做汇总;“商品主题”按 SPU、SKU、类目三种粒度做汇总。上游粒度定得越标准,下游取数越灵活。
3.2 指标标准化:从源头消灭口径冲突
指标口径冲突是所有数仓的通病,但根治它的办法并不复杂:在指标定义阶段统一口径,并把这个口径固化到代码里。业界把这种做法称为“指标字典”或“指标中台”,底层思想就是一句话——先定义清楚,再写代码。
我自己的习惯是这样。每个指标在建表之前,先填一张指标定义表,字段包括:指标名称、指标编码、所属主题域、指标口径(必须精确到 SQL 级别的过滤条件)、统计周期、粒度、单位。举个例子:
指标名称:当日支付成功金额
指标编码:pay_amt_d
指标口径:订单状态 = 已支付 且 支付时间在当日 且 剔除内部测试订单
统计周期:自然日
粒度:用户 + 商品 + 店铺
单位:元
有了这种明确的定义,开发人员写 SQL 时严格按照定义来,测试人员验收时也严格按照定义来。即使未来指标口径需要调整,也只需要修改公共层的加工逻辑,所有下游自动同步,不需要一个报表一个报表地去改。
3.3 命名规范:一张表名看出的专业度
命名规范看着像小事,实际上直接决定了数仓的可维护性和复用效率。命名混乱的数仓,找一张表要靠猜,好不容易找到了,还不确定是不是自己要的,复用率当然上不去。
我推荐的命名规范是“分层前缀 + 主题域 + 业务过程 + 粒度 + 刷新周期”。比如 dwd_trade_order_di,表示 DWD 层、交易域、订单业务过程、日粒度、增量刷新。再比如 dws_user_user_daily_df,表示 DWS 层、用户主题、用户每日汇总、全量刷新。
字段命名也需要统一。是 id 还是 code?是 amount 还是 amt?是 create_time 还是 gmt_created?在项目开始前就要定好标准。最好做一张字段命名参考表,比如金额统一用 amount 加前缀(pay_amount、refund_amount、order_amount),时间统一用 _time 后缀(create_time、pay_time、update_time)。这样做的目的是让业务方和开发人员拿到表后扫一眼字段名就能猜出用途,不需要反复看注释。
4. 从设计到落地:构建过程中的几个关键环节
框架和规范定了,真正动手构建时你会发现,还有一堆具体的问题需要处理:怎么选公共层的技术方案、怎么管理数据血缘、怎么保证调度顺利。这些环节既影响复用性的实现程度,也影响系统运行效率。
4.1 公共层的技术选型:视图、存储过程还是建模工具
构建公共层,业界有不同的技术路线。一种是在数据库里建视图,好处是逻辑统一、维护简单,坏处是性能堪忧,尤其在大数据量的场景下,多层视图嵌套查询能把集群拖死。另一种是全部用物理表,好处是查询性能好,坏处是存储开销大、加工环节多、容易产生大量中间表。还有一种是用专门的建模工具(如 dbt、DataWorks 的数据建模模块),高效但不一定适合所有团队。
我个人的建议是“混合策略”:明细层和汇总层用物理表,因为它们是高频访问对象,需要性能保障;一些低频率、维度多变的取数场景,可以用视图或逻辑模型来支撑,减少不必要的物理存储。生产环境中,把“把不常用的维度查询做成视图”的做法,在存储和性能之间取了一个比较合理的平衡点。
4.2 调度依赖的编排:让公共层先于应用层产出
高复用性数仓对调度系统的要求比普通数仓高得多。由于公共层被大量下游依赖,它的产出时间直接影响全链路的数据时效。如果 DWD 层在上午 10 点才跑完,那么所有基于它的下游任务就只能等到 10 点之后才能启动,整个数据链路都会被拉长。
所以调度编排的原则是:优先保障公共层任务,把它安排在最早的时间窗,并且做好失败重试和告警机制。我在实际项目里使用依赖拓扑管理,明确每个任务的上游和下游,确保任务不会出现无依赖的空转,也不会出现依赖缺失的失败。 公共层任务失败时,必须第一时间告警到责任人,而不是等到下游一批任务跟着失败以后再由人来排查。
4.3 元数据管理与数据血缘:让“可复用”变成“找得到”
一个数仓模型设计得再好,如果别人找不到它,或者不知道它能提供什么数据,复用依然无法发生。很多大公司的数仓团队,实际上很早就建设了元数据中心,把表结构、字段注释、业务负责人、血缘关系都录入系统。但到了中小团队那边,却经常跳过这一步,觉得“表也不多,有什么好管理的”。这是个大误区。
我的经验是,从一开始就要管理血缘。在没有血缘的情况下,一个模型修改后,根本不知道会影响多少下游,只能靠公告让大家自查,很容易漏。现在主流的大数据平台都自带血缘解析能力,也可以借助开源工具实现自动化采集。
5. 常见问题与排查技巧实录
无论设计阶段考虑得多周全,落地过程中还是会遇到各种各样的问题。这一节我把自己遇到过的、在社区里高频讨论的一些坑整理出来,每个问题都给出排查思路和解决办法。
5.1 模型腐化:复用性变差的信号
“模型腐化”是我最想提醒大家的一个问题。它的表现是:公共层模型本来设计得很好,结果某些团队为了图省事,绕过公共层直接基于 ODS 出数;或者因为需求催得紧,直接在公共层表上随便加字段,加到最后公共层变成一个大杂烩。
面对这种情况,单纯靠“提高自觉性”是没用的,必须用机制来约束。我的做法是两条:第一,在数据开发平台上设置规则,ODS 表只能被 DWD 层任务引用,检测到直接引用就告警;第二,公共层模型变更必须走评审流程,评估影响范围后才能上线。前者属于硬限流,后者属于流程约束,两者结合,能够守住公共层的底线。
5.2 口径对不上:谁也说不清谁是对的
口径冲突是数据团队和业务团队之间最常见的矛盾来源。同一份数仓数据,前一天的报表和当天的报表对不上,A 团队的取数和 B 团队的取数对不上,最后相关负责人只能靠人际关系来决定“到底以谁为准”。
这类问题排查起来,要按“从底往上”的思路来。先对比两个取数 SQL 的过滤条件,再对比基础表是否一致,最后看底层源数据是否有变化。80% 的口径冲突出在过滤条件上,比如一个带了“只统计已支付订单”的条件,另一个没带;或一个排除了测试订单,另一个没排除。解决的根本方法还是回到指标字典,把口径定义统一到源头上。
5.3 性能与复用的博弈:宽表是不是越宽越好
公共层经常以宽表形式存在,把多个主题的数据整合到一张表中。优点很明显:下游取数不需要 join,查询效率高。但过度设计宽表,也会带来严重问题:字段上百个,经常使用的只有十几二十个,资源大量浪费;一个字段变更导致整张表重刷,成本极高。
我的经验是,宽表不是越宽越好,而是“按需聚合”。常见的做法是把一个业务过程的核心字段和常用维度放进去,非高频字段保持独立。以订单表为例,订单金额、商品、买家、卖家、店铺这些高频字段必须冗余到宽表;但“优惠券面额”这种低频字段,就不要强行塞进来。这种取舍可能在单个需求上显得不够“一步到位”,但从全局来看,性价比最高。
6. 写在最后的经验体会
做数据仓库和写业务代码很不一样。写业务代码,功能上线就算交付;做数仓,模型上线只是开始,后续每一个新需求都是对它复用性的检验。设计时多花时间想清楚分层、规范、口径,建设时坚持公共层优先,运维时定期检查血缘和引用关系,这三点做到了,数仓的复用性不会差到哪里去。
我个人在实际操作中还有一个体会:复用性最好的一层,往往是建设时最愿意“浪费”时间打磨的一层。DWD 层多花一周做业务过程梳理,后期至少能省一个月的重复开发。这个账,值得每个数仓从业者算清楚。