订阅更新

宽表设计——什么时候该建宽表?什么时候该拆?

数仓开发手册 第 8 题 约 7 分钟读完

本文目录
  1. 先说结论
  2. 业务场景:某短视频平台的用户宽表演化
  3. 段落一:什么时候必须建宽表
  4. 段落二:什么时候该拆宽表——三个明确信号
  5. 段落三:拆表的三种方案对比
  6. 段落四:拆表之后的规范——防止下一个宽表
  7. 面试高频追问
  8. 实战踩坑:一张宽表把 3 个团队的迭代拖到每周一次
  9. 总结

先说结论

宽表的价值是『一次查询不用 JOIN』。BI 看板、交互式分析、ad-hoc 查询这些场景,宽表能把查询延迟从秒级降到毫秒级。

宽表的代价是『更新成本和存储冗余』。任何一个字段变了都要刷全表;字段多了存储几倍放大。

拆宽表的三个信号:字段超过 100 个、日更新量超过 20% 的字段、不同业务域的字段被硬塞到一张表。出现两个就该拆。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

宽表是数仓里最有争议的设计之一。年轻同学爱建宽表——一张 300 个字段的表解决所有查询;有经验的人怕宽表——因为维护噩梦。面试官问这道题想看的是:你知不知道宽表的价值、边界、以及什么时候该拆。回答『都建宽表』或『都拆小表』都是外行。真正的答案是:按业务场景决定。

业务场景:某短视频平台的用户宽表演化

我在某短视频平台做过一张用户宽表 dws_user_wide,上线时 80 个字段,两年后膨胀到 280 个字段,成了 Data Team 最大的维护黑洞——改一次 ETL 要测一整天,任何字段出错都要重跑几小时。最后我主导拆成了三张:用户画像维度、用户行为聚合、用户变现指标。拆完后每张表 60-100 个字段,职责清晰,迭代速度明显提升。

-- 拆前:dws_user_wide 280 字段一张表
CREATE TABLE dws_user_wide (
  uid, register_date, country, device, ...      -- 画像(50字段)
  pv_7d, uv_7d, duration_7d, ...                 -- 行为(100字段)
  gmv_7d, order_cnt_7d, coupon_used_7d, ...      -- 变现(80字段)
  kol_tag, risk_level, ...                       -- 算法标签(50字段)
);  -- 单表 280 字段、跑一次 ETL 4 小时
 
-- 拆后:按业务域拆三张
dws_user_profile     -- 画像(60 字段)
dws_user_behavior    -- 行为(100 字段)
dws_user_monetize    -- 变现(80 字段)
 
-- 下游要跨域的查询就 JOIN
SELECT p.country, SUM(m.gmv_7d)
FROM dws_user_profile p
JOIN dws_user_monetize m USING (uid, dt)
WHERE p.dt = '2024-03-15'
GROUP BY p.country;

段落一:什么时候必须建宽表

场景为什么要宽表建议字段数上限
BI 看板直连查询响应要秒级,JOIN 代价承受不起50-80 字段
ad-hoc 交互分析用户点一下就要出结果80-120 字段
机器学习特征工程模型训练要一次取几百列200-500 字段
导出到外部系统B2B 交付,对方不做 JOIN按业务需求

关键观察:BI 看板和 ad-hoc 分析的字段数应该控制在 100 以内,超过就要考虑拆。机器学习特征宽表例外,特征工程本来就要几百列,这是机器学习特有的范式。

段落二:什么时候该拆宽表——三个明确信号

信号一:单表字段超过 100 个。字段多不是原罪,但超过 100 个基本意味着这张表承担了多个业务域。

信号二:日更新量超过 20% 的字段。宽表的致命伤是『一个字段变了全表重写』。如果 100 个字段里 30 个每天要更新,那每天全表重写的计算成本非常高。

信号三:不同团队在改同一张表。画像组、变现组、算法组都在改同一张宽表,上线 merge 冲突不断,谁也不敢改——这种组织摩擦是拆表的最强信号。

Plain

拆表触发条件检查表:
 
□ 字段数 > 100?
□ 每天更新的字段 > 20%?
□ 2+ 个业务域的字段混在一起?
□ 不同团队在同一张表上改代码?
□ 单次 ETL 时长 > 2 小时?
□ 字段含义分歧、命名冲突越来越多?
 
命中 2+ 项就要认真考虑拆表。

段落三:拆表的三种方案对比

方案一:按业务域垂直拆分。画像、行为、变现各一张。最常见、最清晰。

方案二:按更新频率拆分。稳定字段一张、高频更新字段一张。适合宽表里字段混更新频率的场景。

方案三:按粒度水平拆分。用户日、用户周、用户月各一张。适合周期性汇总占大头的情况。

方案适用场景下游改造成本
按业务域拆字段数太多、多团队维护高(SQL 要加 JOIN)
按更新频率拆高频更新少、稳定字段多低(JOIN 两张即可)
按粒度拆周/月聚合占查询主力低(下游直接选对应粒度)

段落四:拆表之后的规范——防止下一个宽表

拆完以后最怕的是过一年又堆成一张宽表。要建立规范:

规范一:新增字段前先问『这字段属于哪张表』。如果不属于现有任何一张,不应该往现有表里塞,应该评估是否建新表。

规范二:代码 review 卡死字段数。在团队规范里写『单表字段数超过 100 需要评审』,强制大家反思。

规范三:定期字段使用审计。每季度扒一次 SQL 日志,统计每个字段被查询的频率。长期没被查的字段 deprecate。

面试高频追问

追问一:宽表和大宽表有什么区别?

大宽表是宽表的极端形态,字段 500+、覆盖整个业务域(比如『用户全域宽表』把所有用户相关字段全放进来)。大宽表在机器学习特征场景有必要,但在 BI 场景基本是 anti-pattern——会膨胀到几百 TB、ETL 跑几小时、一个字段出错整表重跑。我的态度是:BI 场景最多到 100-120 字段,要上 500 字段就拆。

追问二:如果业务方要求『我就要一张表 JOIN 所有东西』怎么沟通?

这是真实的沟通难题。我的做法是两步:(1)给业务方提供『视图层宽表』——底层是拆开的,用视图或物化视图对外呈现成一张大宽表,业务方感知不到拆分;(2)看板平台上做预 JOIN,业务方查的是看板而不是原始表。这样两边都舒服——业务方看到的是一张表,维护方看到的是拆开的物理表。

追问三:宽表的字段怎么命名防止冲突?

三个规则:(1)加业务前缀——比如 monetize_gmv_7d、behavior_pv_7d;(2)加时间窗后缀——pv_1d / pv_7d / pv_30d;(3)建命名注册表(metadata)——所有字段命名过规范,不允许自由发挥。我在某出海硬件品牌做埋点治理时就是这套打法,1000+ 事件精简到 100+ 标准化事件,数据质量问题减少 60%。

实战踩坑:一张宽表把 3 个团队的迭代拖到每周一次

我在某短视频平台做那张 280 字段宽表时,最痛的一件事是『上线节奏被迫拉长到每周一次』——因为三个团队都在同一张表上改代码,任何一个团队上线都要先和其他团队对齐,冲突解决、回归测试、联合灰度,整个周期 3-4 天。每周只能发一次版。

拆表之后,画像、行为、变现各自独立的 ETL,互不影响,每个团队每天都可以自己上线。这个收益是技术之外最大的——组织敏捷度。面试时要能把『拆表的价值』讲到组织层面,不只停留在性能。

Plain

拆前的协作成本:
  3 个团队 × 共用 1 张宽表 → 任何一个上线都要对齐
  上线周期:1 周
  冲突数:平均每周 2-3 次
 
拆后的协作成本:
  3 个团队 × 各自 1 张表 → 独立上线
  上线周期:按团队节奏(1-2 天)
  冲突数:几乎为零

总结

宽表题考的是 trade-off 判断力。面试时能讲清楚『什么场景值得建宽表、什么信号触发拆表、拆表有几种方案』,再补一个真实的组织层面收益故事(比如拆表让三个团队独立上线),这题就拿到资深水平。最忌讳的是『宽表不好』或『宽表最好』这种一刀切答案。

下一篇:『拉链表怎么实战?——如何用一张表保留完整历史又不爆炸存储?』——建模题第三道,也是数仓里最经典的 SCD Type 2 方案。

你们的宽表有多少字段?有没有那种『加进去三年没人敢删』的遗产字段?

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器