sku必须嵌套在商品主文档中,不可拆分为独立集合;因库存扣减、价格变更等需强一致性操作,拆分将导致跨集合事务开销大、$lookup性能差、原子更新困难,且attrs须为扁平对象、字段名小写下划线、值来自枚举,高频筛选字段如skus.attrs.color、skus.stock等须建复合索引。

SKU必须嵌套在商品主文档中,别拆成独立集合——否则库存扣减、价格变更、上下架都会变慢且容易出错。
为什么不能把 SKU 单独建集合
电商场景下,sku 和 product 是强一致性关系:一次下单可能同时影响多个 SKU 的 stock、status、price。如果拆成两个集合:
- 每次库存扣减都要跨集合事务(MongoDB 4.0+ 才支持),性能差、超时风险高
- 分页查商品列表 + 展示可售 SKU 时,得先查
products,再对每条结果$lookup拉skus,网络往返次数爆炸式增长 - 无法用单个
updateOne原子更新skus.$.stock和skus.$.status,得靠应用层加锁或重试,逻辑复杂易出错
skus 数组里 attrs 字段怎么定义才高效
attrs 必须是扁平对象,不能是数组或深层嵌套,否则索引失效、查询写法冗长:
- ❌ 错误写法:
{"attrs": [{"k":"color","v":"Black"}, {"k":"size","v":"M"}]}—— 无法用点号语法直接find({"skus.attrs.k": "color", "skus.attrs.v": "Black"}),聚合也难优化 - ✅ 正确写法:
{"attrs": {"color": "black", "size": "m"}}—— 可建复合索引{"skus.attrs.color": 1, "skus.attrs.size": 1},支持find({"skus.attrs.color": "black", "skus.attrs.size": "m"}) - 所有属性名(如
color、size、capacity)必须固定,禁止运行时传"颜色"或"Colour";值必须来自后台枚举(如["black", "white", "blue"]),避免大小写/空格/翻译差异污染数据
哪些字段该放外层 product,哪些必须塞进每个 sku 子文档
目标是减少冗余、控制文档大小,同时不牺牲查询效率:
- 共享字段(如
weight、shipping_days、brand)一律放product外层,不要重复存进每个skus元素 - 每个 SKU 独有字段(
sku_id、price、stock、status、attrs)必须存在子文档内,否则筛选和更新无法精准定位 - 注意 MongoDB 单文档 16MB 限制:若一个商品有 500+ SKU,且每个都带大图 URL 或长描述,可能触顶;此时应评估是否真需全部加载,或改用分页拉取
skus子集
高频查询字段必须单独建索引,别指望默认索引扛压
用户按颜色筛选、按价格排序、按库存状态过滤——这些操作若没对应索引,集合稍大(>10 万商品)就会 COLLSCAN 超时:
- 必备索引示例:
{"skus.attrs.color": 1, "skus.status": 1}、{"skus.price": 1, "skus.stock": 1} - 复合索引顺序很重要:等值查询字段放前(如
color),范围/排序字段放后(如price) - 别在
skus数组上建单字段索引(如{"skus": 1}),意义不大;MongoDB 自动为数组字段创建多键索引,但真正起效的是带具体路径的索引 - 用
explain("executionStats")验证查询是否命中索引,尤其注意nReturned和totalDocsExamined是否接近
最容易被忽略的是 attrs 字段的枚举校验和索引覆盖——字段名不统一、值没归一化,前端筛出来的 SKU 就是错的;索引没建对,用户划拉两页就卡住,根本等不到“加载更多”。











