事务事实表粒度:单事务、多事务与设计要点

聊到事实表粒度,我个人的经验是——粒度选对了,整个数仓就稳了一半。尤其是事务事实表,它记录的是业务系统里最细粒度的操作事件。你想想看,每一笔订单、每一次点击、每一笔转账,都是事务事实表的素材。

但问题来了:粒度到底该定多细?是每个事件一条记录,还是把多个事件揉在一起?这就是我们今天要聊的——单事务粒度和多事务粒度。

核心观点:事务事实表的粒度,决定了你能回答什么业务问题,也决定了数据量级和查询性能。选错了,后面改起来非常痛苦。

什么是事务事实表?

先简单回顾一下。事务事实表,记录的是业务系统中每个原子事件。比如:

  • 电商:用户下单、支付、退款
  • 金融:账户转账、ATM取款
  • 物流:包裹扫描、签收

每条记录对应一个事件,时间戳精确到秒甚至毫秒。嗯,这里要注意——事务事实表通常是最细粒度的事实表,也是数据量最大的。

单事务粒度

单事务粒度,说白了就是一张事实表只记录一种业务事件。比如:

  • fact_order_create:只记录订单创建事件
  • fact_order_pay:只记录订单支付事件
  • fact_order_refund:只记录订单退款事件

每张表只关注一个动作,粒度非常清晰。

优点

  • 理解简单:一张表对应一个业务动作,业务人员一看就懂
  • ETL逻辑清晰:从源系统抽取、清洗、加载,流程很直白
  • 查询性能好:表结构简单,索引容易设计

缺点

  • 表数量多:业务事件多了,事实表数量也跟着膨胀
  • 跨事件分析麻烦:想分析从下单到支付的转化率,得join多张表

我的经验:在电商项目中,我一开始用了单事务粒度,订单创建、支付、退款各一张表。后来业务方要分析“下单后多久支付”,每次都要join两张千万级的大表,查询慢得让人抓狂。后来我改成了多事务粒度,问题才解决。

多事务粒度

多事务粒度,就是把多个相关事件放在同一张事实表里。比如:

  • fact_order_transaction:同时记录订单创建、支付、退款、取消等事件

每行记录一个事件,但通过event_type字段区分不同的事件类型。

优点

  • 表数量少:一张表覆盖多个业务事件
  • 跨事件分析方便:同一个订单的不同事件都在一张表里,join操作少
  • 维护成本低:只需要管理一张大表

缺点

  • 表结构复杂:不同事件可能有不同的度量字段,需要设计通用字段或扩展字段
  • 数据量大:所有事件都堆在一张表里,数据量会快速增长
  • 查询可能变慢:如果过滤条件不当,全表扫描会很痛苦
对比维度 单事务粒度 多事务粒度
表数量 多 少
表结构 简单 复杂
跨事件分析 需要join 直接查询
数据量 分散 集中
维护成本 高 低
适用场景 事件类型差异大 事件类型相似

事务粒度设计要点

好了,理论讲完了。接下来聊聊我在项目中踩过的坑,以及总结出来的设计要点。

要点一:明确业务过程

设计之前,先搞清楚业务方到底要分析什么。我曾经遇到一个需求,业务方说“我要看订单的全生命周期”。结果我设计了单事务粒度,他们每次都要join三张表。后来我改成多事务粒度,一张表搞定。

怎么判断?问自己三个问题:

  1. 业务方最常分析的维度是什么?
  2. 他们需要跨事件对比吗?
  3. 数据量级能接受吗?

要点二:控制粒度层级

粒度越细,数据越灵活,但数据量也越大。我见过一个项目,把用户每次页面滚动都记录成一条事务事实,结果一天就产生了10亿条数据。嗯,这显然过度设计了。

我的建议:粒度细到能回答业务问题就行,不要为了“万一以后要用”而过度设计。

要点三:设计事件类型字段

如果选择多事务粒度,一定要设计好event_type字段。我习惯用枚举值,比如:

event_type:
  1 - 订单创建
  2 - 订单支付
  3 - 订单退款
  4 - 订单取消

这样查询时可以直接过滤,性能更好。别用字符串,字符串查询慢,还容易出错。

要点四:处理不同事件的度量差异

不同事件可能有不同的度量字段。比如:

  • 订单创建:订单金额、商品数量
  • 订单支付:支付金额、支付方式
  • 订单退款:退款金额、退款原因

我的做法是:通用字段放主表,特殊字段用扩展表或JSON字段。比如:

-- 主表
fact_order_transaction (
  transaction_id,
  order_id,
  event_type,
  event_time,
  amount,        -- 通用金额字段
  quantity       -- 通用数量字段
)

-- 扩展表(可选)
fact_order_transaction_ext (
  transaction_id,
  payment_method,   -- 支付方式
  refund_reason     -- 退款原因
)

避坑指南:我曾经在一个项目里把所有度量字段都放在一张表里,结果表宽达到了200多列,查询性能极差。后来我拆成了主表和扩展表,性能才恢复正常。记住:表不要太宽,宽表不是万能的。

要点五:考虑数据生命周期

事务事实表的数据量增长很快。我建议:

  • 热数据:近3个月的数据,放在高性能存储
  • 温数据:3-12个月的数据,放在普通存储
  • 冷数据:1年以上的数据,归档到低成本存储

这样既能保证查询性能,又能控制存储成本。

知识体系总览

下面这张图,帮你快速理清事务事实表粒度的核心逻辑:

事务事实表粒度选择框架 事务事实表粒度选择框架 事务事实表 单事务粒度 多事务粒度 一张表只记录一种事件 表结构简单,查询性能好 一张表记录多种事件 跨事件分析方便 设计要点 ① 明确业务过程 ② 控制粒度层级 ③ 设计事件类型字段 ④ 处理度量差异 ⑤ 考虑数据生命周期

总结一下

事务事实表的粒度选择,没有的对错。我的经验是:

  • 事件类型差异大(比如订单和退款逻辑完全不同),用单事务粒度
  • 事件类型相似(比如不同支付渠道的支付事件),用多事务粒度
  • 不确定时,先选单事务粒度,后期可以通过视图或物化视图合并

记住:粒度设计是数仓的基石。选对了,后面一路顺畅;选错了,改起来成本极高。嗯,今天就聊到这里,希望对你有所帮助。