深入浅出:电商系统中的 SPU 与 SKU 概念解析
·
深入浅出:电商系统中的 SPU 与 SKU 概念解析
前言
在电商系统开发中,SPU 和 SKU 是两个最基础也最常被混淆的概念。无论你是产品经理、后端开发还是前端同学,搞清楚这两个概念对理解商品体系至关重要。
本文将从概念、关系、数据建模到实战场景,帮你一次性吃透 SPU 和 SKU。
一、概念定义
1.1 什么是 SPU?
SPU = Standard Product Unit(标准化产品单元)
SPU 指的是一个商品的抽象品类,是独立于具体规格的「商品主体」。它的核心特征是:属性基本相同的商品集合。
举例:
| SPU | 说明 |
|---|---|
| iPhone 16 Pro Max | 不论颜色、存储容量,都属同一 SPU |
| Nike Air Force 1 经典款 | 不论尺码、颜色,都属同一 SPU |
| 《三体》刘慈欣著 | 不论精装/平装,都属同一 SPU |
一句话:SPU = “这东西是什么”
1.2 什么是 SKU?
SKU = Stock Keeping Unit(库存保有单位)
SKU 指的是一个具体可售卖的商品变体,是库存管理的最小粒度。每个 SKU 对应唯一的规格组合,拥有独立的库存和价格。
举例:
以「iPhone 16 Pro Max」这个 SPU 为例,它的 SKU 就有:
| SKU | 规格组合 | 库存 |
|---|---|---|
| AP-IP16PM-BK-256 | 黑色 + 256G | 200 台 |
| AP-IP16PM-WT-256 | 白色 + 256G | 150 台 |
| AP-IP16PM-BK-512 | 黑色 + 512G | 80 台 |
| AP-IP16PM-WT-512 | 白色 + 512G | 60 台 |
一句话:SKU = “具体买的是哪个”
二、核心区别一览
| 维度 | SPU | SKU |
|---|---|---|
| 粒度 | 粗(品类层) | 细(单品层) |
| 唯一标识 | SPU ID | SKU ID |
| 库存管理 | 无库存 | 有独立库存 |
| 价格 | 价格区间/参考价 | 具体售价 |
| 规格属性 | 共用属性(品牌、型号) | 规格属性(颜色、尺寸) |
| 图片 | 主图/详情图 | 规格图(可选) |
| 典型场景 | 商品详情页 | 加购物车/下单 |
三、实体关系模型
一个 SPU 包含多个 SKU,这是一对多的关系。
┌──────────────────────────────────┐
│ SPU │
│ - spu_id: string (PK) │
│ - name: string │
│ - brand: string │
│ - category_id: string │
│ - description: text │
│ - main_image: string │
│ - created_at: datetime │
└──────────────┬───────────────────┘
│ 1
│
│ 包含
│
│ N
┌──────────────────────────────────┐
│ SKU │
│ - sku_id: string (PK) │
│ - spu_id: string (FK) │
│ - price: decimal │
│ - stock: int │
│ - specs: JSON (规格组合) │
│ - sku_image: string │
│ - status: enum (上架/下架) │
│ - created_at: datetime │
└──────────────────────────────────┘
数据库表设计示例
-- SPU 表
CREATE TABLE spu (
spu_id VARCHAR(32) PRIMARY KEY,
name VARCHAR(200) NOT NULL,
brand VARCHAR(100),
category_id VARCHAR(32),
description TEXT,
main_image VARCHAR(500),
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- SKU 表
CREATE TABLE sku (
sku_id VARCHAR(32) PRIMARY KEY,
spu_id VARCHAR(32) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
specs JSON NOT NULL, -- {"颜色": "黑色", "存储": "256G"}
sku_image VARCHAR(500),
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (spu_id) REFERENCES spu(spu_id)
);
-- 规格属性表(可选,用于管理规格模板)
CREATE TABLE spec_template (
id INT PRIMARY KEY AUTO_INCREMENT,
spu_id VARCHAR(32),
spec_name VARCHAR(50), -- "颜色" / "尺寸" / "容量"
spec_values JSON, -- ["黑色","白色","蓝色"]
FOREIGN KEY (spu_id) REFERENCES spu(spu_id)
);
四、实战场景分析
场景 1:商品详情页
页面加载时:
- 查询 SPU:获取商品名称、描述、主图、品牌等基础信息
- 查询 SKU 列表:获取所有规格变体
- 前端组合规格:渲染规格选择器
// 前端伪代码
const spu = await getSpuById(spuId); // SPU 基本信息
const skuList = await getSkuList(spuId); // 所有 SKU
// 提取规格选项
const specOptions = extractSpecOptions(skuList);
// → { "颜色": ["黑色", "白色"], "存储": ["256G", "512G"] }
// 用户选择规格 → 匹配对应 SKU
function matchSku(color: string, storage: string): Sku {
return skuList.find(sku =>
sku.specs["颜色"] === color &&
sku.specs["存储"] === storage
);
}
场景 2:购物车与下单
购物车和订单只关心 SKU,不关心 SPU:
interface CartItem {
skuId: string; // 具体通哪个 SKU
spuId: string; // 关联的 SPU(用于展示商品名、图片)
quantity: number;
price: number; // SKU 的当前价格
}
// 库存校验只针对 SKU
function checkStock(skuId: string, quantity: number): boolean {
const sku = skuRepository.findById(skuId);
return sku.stock >= quantity;
}
场景 3:促销活动
促销可以挂在 SPU 级别(全场通用),也可以挂在 SKU 级别(清特定库存):
interface Promotion {
id: string;
type: 'spu' | 'sku'; // 作用范围
targetId: string; // spu_id 或 sku_id
discountType: 'percent' | 'fixed';
discountValue: number;
}
// 计算价格
function calcPrice(sku: Sku, promotions: Promotion[]): number {
let price = sku.price;
// SPU 级促销(全场满减、折扣)
const spuPromo = promotions.find(p => p.type === 'spu' && p.targetId === sku.spuId);
if (spuPromo) price = applyDiscount(price, spuPromo);
// SKU 级促销(清仓特价)
const skuPromo = promotions.find(p => p.type === 'sku' && p.targetId === sku.skuId);
if (skuPromo) price = applyDiscount(price, skuPromo);
return price;
}
场景 4:库存预警
库存管理只出现在 SKU 层级:
-- 查询库存不足的 SKU
SELECT sku_id, specs, stock
FROM sku
WHERE stock < 10
AND status = 1
ORDER BY stock ASC;
-- 按 SPU 聚合查看库存情况
SELECT spu_id, COUNT(*) AS sku_count, SUM(stock) AS total_stock
FROM sku
GROUP BY spu_id
HAVING total_stock < 100;
五、前端交互设计要点
5.1 规格选择逻辑
┌─ 商品信息(SPU)─────────────────────┐
│ iPhone 16 Pro Max │
│ 价格: ¥7999 起 │ ← 显示价格区间
│ 已选: 黑色 / 256G │
├───────────────────────────────────────┤
│ 颜色: ● 黑色 ● 白色 ○ 蓝色 │ ← 停售/缺货灰色不可选
│ 存储: [256G] [512G] [1TB] │
├───────────────────────────────────────┤
│ 实付: ¥7,999.00 │ ← 选中完整规格后显示具体价格
│ [加入购物车] │
└───────────────────────────────────────┘
5.2 高难度:SKU 矩阵联动
当有多个规格维度时,需要实现矩阵联动:
规格: 颜色 × 存储 × 版本(国行/美版)
选中: "黑色" + "256G" + "国行"
→ 唯一 SKU,显示价格和库存
联合禁用逻辑:
选 "黑色" 后,存储选项中 "1TB" 可能无货 → 置灰
再选 "国行" 后,可能只有 "256G" 有货 → 其余置灰
常见实现方案:
- 笛卡尔积遍历匹配:小规格维度时可用
- SKU Map 预计算:提前建立所有规格组合的映射表
- GraphQL 按需查询:前端选择变化时动态查剩余库存
六、常见误区
| 误区 | 正确理解 |
|---|---|
| SKU 就是商品条码 | SKU 是内部管理编码,商品条码(UPC/EAN)是外部标准编码 |
| 一个商品只有一个 SKU | 有规格的商品会有多个 SKU |
| SKU 和 SPU 可以互换 | 粒度不同,不可互换 |
| 所有商品都需要 SPU/SKU 分离 | 简单商品(无规格)可以 SPU = SKU |
七、总结
| 层次 | 职责 | 谁关心 |
|---|---|---|
| SPU | 商品抽象、信息展示、归类 | 运营、前端展示 |
| SKU | 库存管理、价格计算、订单履约 | 仓储、财务、后端逻辑 |
记住这句口诀:
SPU 是「卖的是什么」,SKU 是「卖的是哪个」。
设计商品系统时,先从这两个概念入手,理清各自的职责边界,后续的扩展(多规格、多渠道库存、促销分摊等)就会顺畅很多。
更多推荐



所有评论(0)