订阅更新

累计快照事实表怎么设计?

面试问 046 约 3 分钟读完 2 次阅读

本文目录
  1. 面试怎么回答
  2. 第一步:确定稳定粒度
  3. 第二步:定义流程里程碑
  4. 第三步:设计持续更新机制
  5. 第四步:处理回退、跳步和超时
  6. 一个典型反例

面试怎么回答

我会先确定稳定流程粒度和业务主键,再定义创建、支付、发货、完成等里程碑与阶段耗时。同一流程只保留一行,事件到达后通过幂等 MERGE 持续更新,并用事件时间、版本和当前状态处理乱序与重复。还要明确回退、跳步、拆单和超时规则。核心是让每个时间字段都有唯一业务含义,而不是简单把状态时间拼在一行。

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

详细讲解

很多人会回答:

“一行记录一个订单,把各阶段时间放在同一行。”

这个定义没错,但设计难点在于:一行到底代表什么流程、哪些里程碑会持续更新、乱序和重复事件到达后如何保证最终状态正确。

累计快照事实表适合有明确起点和终点的流程,用一行持续记录同一业务实例的里程碑与阶段耗时。


第一步:确定稳定粒度

先明确“一行代表什么”。例如:

  • 一行一个订单履约流程
  • 一行一次工单处理流程
  • 一行一个广告投放审批流程

粒度必须有稳定业务主键。一个订单如果允许多次发货,就要判断是一行一个订单,还是一行一个履约单,不能只凭表名决定。


第二步:定义流程里程碑

累计快照更新过程

以订单履约为例,可以保存:

created_time
paid_time
shipped_time
delivered_time
closed_time

同时计算阶段耗时:

  • 支付耗时 = paid_time - created_time
  • 发货耗时 = shipped_time - paid_time
  • 配送耗时 = delivered_time - shipped_time
  • 全流程耗时 = closed_time - created_time

未完成阶段保留空值,不要用固定时间或 0 冒充已完成。


第三步:设计持续更新机制

累计快照与只追加的事务事实不同,同一业务实例会随着事件到达不断更新。

常见方式是按业务主键执行 MERGE:

  • 新流程到达时插入一行
  • 新里程碑到达时只更新对应字段
  • 重复事件到达时保持幂等
  • 更晚到达的旧事件不能覆盖更新版本

因此表中通常还会保留 last_event_time、update_time、当前状态和版本信息,用于判断更新顺序。


第四步:处理回退、跳步和超时

真实流程不总是线性前进:

  • 订单可能取消后又恢复
  • 事件可能先到发货、后到支付
  • 某个阶段可能被跳过
  • 流程可能长期停留在中间状态

设计时要明确状态机和字段更新规则,并定期识别超时未完成流程。累计快照不是把所有时间字段放一行就结束,还要保证状态变化有业务语义。


一个典型反例

比如以 order_id 为粒度设计履约累计快照,但一个订单可以拆成多个包裹。

第一个包裹发货后写入 shipped_time,第二个包裹晚两天发货又覆盖该字段。最终这一个时间既不是首次发货,也不是全部发货完成,口径变得模糊。

正确做法是先确认分析目标:如果关注包裹履约,应以履约单或包裹为粒度;如果坚持订单粒度,就必须明确字段是首次发货、末次发货还是全部完成。

累计快照最怕的不是空值,而是里程碑含义不清。


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