4. 事实表设计优化:三种快照的场景选择

事实表设计,说白了就是回答三个问题:记录什么、多久记录一次、怎么记录变化。我做了这么多年数仓,见过太多因为事实表选型翻车的案例。今天咱们就聊聊事务事实表、周期快照事实表、累积快照事实表——这三种到底该怎么选。

核心观点:没有银弹。选哪种取决于你的业务场景是「看过程」「看状态」还是「看全貌」。

4.1 事务事实表:记录每一次「发生」

事务事实表,就是记录业务事件的每一行。比如下单、支付、退款,每发生一次就写一行。这是最基础、最常用的事实表类型。

适用场景:

  • 需要分析单个事件的明细(比如某笔订单的金额)
  • 事件之间没有强依赖关系
  • 数据量可控,不需要频繁聚合

我个人的习惯是:只要业务方说「我要看每一笔交易的详情」,我第一反应就是事务事实表。但要注意,它有个致命弱点——无法直接反映状态变化。

避坑指南:我曾经接手过一个项目,业务方要求「统计每天有多少订单处于'已发货'状态」。结果他们用的事务事实表,每次发货都只记录一条新行。要算「当前已发货」还得去重、取最新状态……嗯,后来我给他们改成了周期快照。

4.2 周期快照事实表:定期「拍照」

周期快照事实表,就是按固定时间间隔(比如每天、每小时)记录业务对象的当前状态。它不关心中间发生了什么,只关心「这一刻是什么样」。

适用场景:

  • 需要分析某个时间点的存量或状态(比如库存、账户余额)
  • 业务状态变化频繁,但不需要逐笔追溯
  • 数据量随时间线性增长,可控

举个例子:库存表。你不需要记录每一次入库出库的明细(那是事务事实表的事),你只需要知道每天结束时仓库里还剩多少货。周期快照就是干这个的。

注意:周期快照的粒度是「时间+维度」。比如每天每个仓库每个SKU一条记录。如果维度组合太多,数据量会爆炸。我见过一个项目,每天跑一次全量快照,结果一个月后表就几百亿行……后来改成了增量快照才救回来。

4.3 累积快照事实表:追踪「全生命周期」

累积快照事实表,专门用来追踪一个业务流程从开始到结束的完整生命周期。它只有一行记录,但这一行会随着流程推进不断更新。

适用场景:

  • 需要分析流程耗时(比如订单从下单到签收花了几天)
  • 流程有明确的开始和结束节点
  • 需要同时看到多个里程碑的状态

我记得有个电商客户,他们想分析「从下单到发货平均需要多久」。如果用事务事实表,你得把下单、支付、发货、签收这些事件关联起来,写一堆复杂的SQL。用累积快照就简单了——每个订单一行,记录每个里程碑的时间戳,直接算差值就行。

关键设计:累积快照事实表通常包含多个日期字段(比如下单日期、支付日期、发货日期、签收日期),以及对应的状态字段。每次流程推进,就更新对应字段。

4.4 三种事实表的对比

为了让你看得更清楚,我整理了一张对比表:

维度 事务事实表 周期快照事实表 累积快照事实表
记录粒度 每笔事件 每个周期每个维度组合 每个流程实例
数据更新方式 只追加 覆盖或追加 更新已有行
典型场景 交易流水、日志 库存、余额、日活 订单生命周期、工单
查询特点 明细查询、聚合 时间点快照查询 流程耗时、里程碑分析
数据量 随事件量增长 随周期和维度增长 随流程实例增长
ETL复杂度 低 中 高(需要处理更新)

4.5 如何选择?一个决策框架

你可能会问:那我到底该用哪种?我一般按这个思路来:

  1. 先问业务问题:你要分析的是「事件」「状态」还是「流程」?
  2. 再看数据特征:数据是只增不减,还是需要更新?
  3. 最后评估成本:ETL复杂度、存储成本、查询性能,哪个是瓶颈?

举个例子:

  • 如果业务方说「我要看每天有多少新用户注册」——用事务事实表,记录每次注册事件。
  • 如果业务方说「我要看每天有多少活跃用户」——用周期快照,每天记录一次用户状态。
  • 如果业务方说「我要看用户从注册到首次下单花了多久」——用累积快照,每个用户一行,记录注册时间和首次下单时间。

我的经验:很多时候,一个业务场景需要组合使用多种事实表。比如订单分析:事务事实表记录每笔交易,周期快照记录每天订单状态分布,累积快照追踪每个订单的完整生命周期。三者互补,才能覆盖所有分析需求。

4.6 三种事实表的逻辑关系

下面这张图展示了三种事实表在业务流程中的位置和关系:

三种事实表的逻辑关系 业务流程:下单 → 支付 → 发货 → 签收 事务事实表 记录每次事件 下单事件、支付事件 发货事件、签收事件 每行:一个事件 周期快照事实表 每天记录一次状态 每天订单状态分布 每天库存快照 每行:一个周期一个维度组合 累积快照事实表 追踪完整生命周期 下单时间、支付时间 发货时间、签收时间 每行:一个订单 三种事实表互补,覆盖不同分析需求 事务事实表:回答「发生了什么」 周期快照事实表:回答「当前状态是什么」 累积快照事实表:回答「整个过程花了多久」

4.7 实战中的常见误区

最后,分享几个我踩过的坑:

  • 误区一:所有场景都用事务事实表。结果就是查询性能差,业务方要个「当前库存」都得跑几分钟。
  • 误区二:周期快照的周期设得太短。比如库存每5分钟拍一次,数据量暴增,但业务根本不需要这么细的粒度。
  • 误区三:累积快照的更新逻辑没处理好。我曾经遇到一个项目,ETL脚本没做幂等处理,导致同一行被重复更新,数据全乱了。

重要提醒:累积快照事实表的更新操作要格外小心。如果并发高,建议用「先查后改」或者「版本号」机制,避免数据覆盖。我一般会在累积快照里加一个last_updated时间戳,用来做冲突检测。

好了,三种事实表的选择就聊到这。记住:没有最好的事实表,只有最合适的。下次设计事实表时,先想清楚业务要什么,再动手。