反规范化的代价是什么
反规范化用冗余换读取效率:把常一起访问的数据放进同一文档或宽表,省掉 join 与多次往返。代价是同一事实存了多份,写入要同步更新多处,失败窗口会带来数据漂移,需要补偿与对账兜底。评估时要把写路径成本也算进去:一次业务写入放大到多个位置更新的代价,可能超过读侧省下的开销。适合冗余的是变更频率低、读取频繁的展示字段。
基本信息
| 适合冗余 | 变更频率低、读取频繁的展示字段(姓名、标题快照、类目名) |
|---|---|
| 不适合冗余 | 价格、库存、余额这类高频变更且要求强一致的值 |
| 搜索侧 | ES 不支持常规 join,常把关联数据拼进文档或用嵌套结构 |
| 维护手段 | 批量回填限速、对账任务、幂等更新 |
典型使用场景
- 订单快照:下单时把用户名、商品标题写进订单
- 列表页提速:宽表里直接带上类目名称,避免每次 join
- 搜索文档:把关联字段拼进检索文档
- 缓存预热:把多表拼装后的结果写入缓存
- 漂移修复:定期跑对账任务发现不一致的冗余值
文档内冗余示例
文档内冗余示例
// 订单里冗余下单时的用户名与商品标题快照
{ _id: 1001, user: { id: 7, name: "ann" }, items: [ { sku: "a", title: "键盘" } ], total: 128 }
// 用户名变更时回填受影响订单(量大时限定时间窗口并限速)
db.orders.updateMany(
{ "user.id": 7, createdAt: { $gte: ISODate("2026-01-01") } },
{ $set: { "user.name": "anne" } }
)评估冗余时把写路径成本也算进去:一次业务写入放大到多个位置更新的代价,可能超过读侧省下的开销。
冗余字段的批量回填不限速会影响线上写入,按主键分段并观察延迟更安全。
没有对账任务时,冗余字段的更新失败会长期存在且很难被发现,直到业务侧报出展示不一致才暴露。
冗余字段要有明确的「以谁为准」约定:源头表是唯一事实来源,冗余副本只能按规则重建,否则各处自行更新会出现谁都不负责的局面。