sku 必须嵌套在 product 文档内以保障库存原子性和查询性能;拆分会导致跨集合事务开销大、$lookup 性能差、强一致性难保证;skus.attrs 应为扁平对象,字段名统一小写下划线,值来自枚举表;需对 skus.attrs.color 等高频筛选字段及 skus.stock、skus.status 建索引;库存更新必须用原子操作如 $inc,并严格校验 sku_id 和 stock > 0。

skus 数组必须嵌套在 product 文档内,不能拆成独立集合——这是电商场景下保证库存原子性和查询性能的底线。
为什么不能把 SKU 单独建集合?
拆分后一次库存扣减要跨集合事务,MongoDB 4.0+ 才支持,但事务开销大、超时风险高;分页查商品列表时,$lookup 会显著拖慢响应,尤其在商品数 >10 万时延迟飙升。更麻烦的是,status、price 变更常需和 stock 同步更新,跨集合很难做到强一致。
skus 数组里怎么存规格字段?
别用数组存键值对(如 [{k:"color",v:"Black"}]),也别嵌套过深(比如 spec.detail.color)。正确方式是让每个 sku 子文档的 attrs 字段为扁平对象:
{ "sku_id": "sku_123-a",
"attrs": { "color": "black", "capacity": "256GB" },
"price": 7999,
"stock": 42,
"status": "in_stock" }
-
attrs必须是对象,不是数组——否则无法用attrs.color建索引 - 字段名统一用英文小写+下划线,禁止运行时传入“颜色”“Color”“colour”等不规范 key
- 值必须来自后台枚举表,例如
color只允许"black"、"white"、"blue" - 所有 SKU 共享的字段(如
weight、shipping_days)提到product外层,别重复塞进每个sku
哪些字段必须建索引?
高频筛选字段不建索引,explain() 一跑全是 COLLSCAN。重点索引这些路径:
-
skus.attrs.color、skus.attrs.capacity等常用规格字段,建单字段索引 - 组合查询场景(如“黑色 + 256GB”)建复合索引:
db.products.createIndex({"skus.attrs.color": 1, "skus.attrs.capacity": 1}) -
skus.stock和skus.status也要单独建索引,用于库存预警或上下架过滤 - 避免对
skus整个数组建索引——MongoDB 会为每个元素生成索引项,膨胀严重
库存更新必须用原子操作
并发抢购时直接读-改-写必然超卖。必须用 $inc 或带 version 的乐观锁:
db.products.updateOne(
{ "_id": ObjectId("..."), "skus.sku_id": "sku_123-a", "skus.stock": { $gt: 0 } },
{ "$inc": { "skus.$.stock": -1 } }
)
- 条件里必须同时匹配
sku_id和stock > 0,否则可能扣成负数 - 别依赖应用层判断库存后再更新——网络延迟+并发会让判断失效
- 如果业务要求严格一致性(如秒杀),建议加
version字段做乐观锁,失败时重试而非直接报错











