← 返回题目列表

快照表应该如何设计?订单为什么要保存商品快照?

中等 第 26 / 33 题 更新于 2026/07/30
快照表订单设计历史一致性

简化版

快照表用于保存业务发生当时的关键数据,避免后续主数据变化影响历史记录。订单保存商品名称、价格、规格、图片等快照,是为了保证订单详情、售后、对账和审计都能还原交易发生时的事实。

详细版

如果订单只存 product_id,页面展示时再查商品表,那么商品改名、改价、换图后,历史订单会被“改写”。这对交易系统是错误的。

常见设计是在订单明细表里冗余一份商品快照字段,或者单独建订单商品快照表。快照字段包括商品标题、SKU 名称、成交单价、数量、营销信息、税率、商家信息等。

快照不是随意冗余。它保存的是“历史事实”,通常不跟随主表更新;如果确实要修正,只能走冲正、备注、审计记录,而不是静默覆盖。

完整版教学

一、为什么历史记录不能总查主表

主表代表当前状态,订单代表历史事实。商品表里的价格是现在的价格,订单里的价格是下单那一刻的成交价格,两者语义不同。

假设用户 10:00 以 99 元买了商品,12:00 商品涨到 129 元。如果订单详情实时查商品价格,就会显示用户买成了 129 元,售后和对账都会出错。

所以订单不能只保存外键,还要保存足够的交易快照。

二、快照字段应该放哪里

小型快照可以直接放在订单明细表。例如商品名、SKU 名、成交价、数量这类高频展示字段,放在 order_item 里查询最方便。

快照字段很多时,可以拆成独立快照表或 JSON 快照字段。拆表能避免订单明细过宽,JSON 适合保存低频展示的扩展信息。

CREATE TABLE order_item (
  id BIGINT PRIMARY KEY,
  order_id BIGINT NOT NULL,
  product_id BIGINT NOT NULL,
  sku_id BIGINT NOT NULL,
  product_name VARCHAR(255) NOT NULL,
  sku_name VARCHAR(255) NOT NULL,
  deal_price DECIMAL(18,2) NOT NULL,
  quantity INT NOT NULL
);

三、快照不是备份整张主表

快照要围绕业务场景选择字段,不是把商品表所有列复制一份。订单需要的是交易、展示、对账和售后必要字段。

例如商品详情页有 80 个字段,订单快照可能只需要 12 个。如果把所有字段都复制,会增加存储、写入和后续字段兼容成本。

字段类型是否适合快照原因
成交价适合对账核心事实
商品名适合历史展示需要
当前库存不适合会变化,非历史事实
商品详情长文视情况售后争议可能需要

四、快照如何处理变更和修正

快照一旦生成,原则上不随主表变化。商品改名、商家换 logo、类目调整,都不应该自动改历史订单。

如果确实发现快照写错,例如价格计算 bug,要通过补偿记录、调整单或审计日志修正,而不是直接 update 历史快照让证据消失。

这体现了一个数据库设计原则:历史事实可以被修正,但修正本身也要留下痕迹。

五、快照和冗余字段的区别

冗余字段常用于性能优化,可能会跟随主数据同步;快照字段用于保留历史语义,通常不跟随更新。

例如用户昵称冗余到评论表,可能希望昵称变化后评论展示也变化;但订单收货人信息是交易快照,不能因为用户改了默认地址就改变历史订单。

这个区别在面试中很关键,因为它决定了一致性策略:冗余要同步,快照要冻结。

六、常见误区与追问

  • 误区:有外键就不需要快照。 外键只能找到当前主数据,不能保证历史事实不变。
  • 误区:快照就是复制主表全部字段。 只保存业务还原需要的关键字段,避免无意义膨胀。
  • 误区:主表改了快照也要同步。 快照的价值就是冻结当时事实,通常不跟随更新。
  • 追问:订单商品快照至少保存什么? 商品名、SKU 名、成交价、数量、商家和必要营销信息。
  • 追问:快照写错怎么办? 走修正单、补偿或审计记录,不能静默覆盖历史证据。

七、加强记忆

记忆钩子:主表是“现在的样子”,快照是“当时的照片”;订单要看照片,不能每次都让商品重新化妆再拍。

回答这题按“历史事实和当前状态分离 → 字段选择 → 冻结语义 → 修正留痕”展开。面试官考的不是冗余字段,而是你是否理解交易系统的历史一致性。