手把手教你设计电商商品中心:从SPU/SKU概念到MySQL表结构实战(附建表SQL)

张开发
2026/4/21 8:50:56 15 分钟阅读
手把手教你设计电商商品中心:从SPU/SKU概念到MySQL表结构实战(附建表SQL)
电商商品中心设计实战从SPU/SKU模型到高扩展性数据库架构当你在电商平台看到iPhone 15 Pro Max 256GB 钛金属色和512GB 原色两款手机时背后是商品中心数据库的精密设计在支撑。作为电商系统的核心模块商品中心的设计质量直接影响着整个平台的运营效率和用户体验。本文将带你从零开始构建一个具备生产级扩展性的商品中心数据库架构。1. 商品模型基础理解SPU与SKU的本质区别在电商领域商品信息管理有两个核心概念SPUStandard Product Unit和SKUStock Keeping Unit。理解它们的区别是设计商品中心的基础。SPU代表标准化产品单元它描述的是产品的身份信息产品名称如iPhone 15 Pro Max产品描述基础参数如操作系统、处理器型号产品分类归属SKU则是库存管理单元它定义了产品的交易属性具体规格如256GB 钛金属色销售价格库存数量商品图片集用一个实际案例来说明某款运动鞋作为一个SPU包含了品牌、系列、材质等固定信息而不同颜色、尺码的组合如黑色42码、白色39码则形成多个SKU。这种设计使得前端可以灵活展示商品信息后端能够高效管理库存和销售数据运营人员可以便捷地维护商品信息关键设计原则SPU承载不变的产品属性SKU管理可变的销售属性。这种分离保证了数据的一致性和灵活性。2. 数据库架构设计核心表结构与关系基于SPU/SKU模型我们设计以下核心表结构2.1 分类体系设计商品分类是商品组织的基础采用三级分类设计CREATE TABLE category ( id bigint NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名称, parent_id bigint DEFAULT NULL COMMENT 父分类ID, level tinyint DEFAULT NULL COMMENT 层级(1-3), sort int DEFAULT 0 COMMENT 排序, icon varchar(255) DEFAULT NULL COMMENT 图标URL, unit varchar(20) DEFAULT NULL COMMENT 计量单位, show_status tinyint DEFAULT 1 COMMENT 显示状态, PRIMARY KEY (id), KEY idx_parent_id (parent_id) ) ENGINEInnoDB COMMENT商品分类表;2.2 属性系统设计商品属性分为基础属性和销售属性CREATE TABLE attribute ( id bigint NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 属性名称, category_id bigint NOT NULL COMMENT 所属分类, type tinyint NOT NULL DEFAULT 0 COMMENT 属性类型(0-基础属性,1-销售属性), value_type tinyint DEFAULT 0 COMMENT 值类型(0-单选,1-多选,2-输入), values varchar(500) DEFAULT NULL COMMENT 可选值列表(分号分隔), searchable tinyint DEFAULT 0 COMMENT 是否可搜索, required tinyint DEFAULT 0 COMMENT 是否必填, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB COMMENT商品属性表;2.3 SPU核心表设计SPU信息表存储产品基本信息CREATE TABLE spu ( id bigint NOT NULL AUTO_INCREMENT, name varchar(200) NOT NULL COMMENT SPU名称, category_id bigint NOT NULL COMMENT 分类ID, brand_id bigint DEFAULT NULL COMMENT 品牌ID, description text COMMENT 商品描述, detail longtext COMMENT 商品详情(HTML), status tinyint DEFAULT 0 COMMENT 状态(0-下架,1-上架), create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_brand (brand_id) ) ENGINEInnoDB COMMENTSPU信息表;SPU属性值表存储产品的基础属性CREATE TABLE spu_attribute_value ( id bigint NOT NULL AUTO_INCREMENT, spu_id bigint NOT NULL, attribute_id bigint NOT NULL, value varchar(255) NOT NULL COMMENT 属性值, PRIMARY KEY (id), UNIQUE KEY uk_spu_attribute (spu_id,attribute_id) ) ENGINEInnoDB COMMENTSPU属性值表;2.4 SKU核心表设计SKU信息表存储具体商品项CREATE TABLE sku ( id bigint NOT NULL AUTO_INCREMENT, spu_id bigint NOT NULL COMMENT 所属SPU, code varchar(50) DEFAULT NULL COMMENT SKU编码, name varchar(200) NOT NULL COMMENT SKU名称, price decimal(10,2) NOT NULL COMMENT 销售价格, cost_price decimal(10,2) DEFAULT NULL COMMENT 成本价, stock int NOT NULL DEFAULT 0 COMMENT 库存数量, lock_stock int DEFAULT 0 COMMENT 锁定库存, sales int DEFAULT 0 COMMENT 销量, default_image varchar(255) DEFAULT NULL COMMENT 默认图片, status tinyint DEFAULT 1 COMMENT 状态(0-禁用,1-启用), PRIMARY KEY (id), KEY idx_spu (spu_id), KEY idx_code (code) ) ENGINEInnoDB COMMENTSKU信息表;SKU属性值表存储商品的销售属性CREATE TABLE sku_attribute_value ( id bigint NOT NULL AUTO_INCREMENT, sku_id bigint NOT NULL, attribute_id bigint NOT NULL, value varchar(255) NOT NULL COMMENT 属性值, PRIMARY KEY (id), UNIQUE KEY uk_sku_attribute (sku_id,attribute_id) ) ENGINEInnoDB COMMENTSKU属性值表;3. 高级设计解决电商业务的复杂场景3.1 多规格SKU生成算法当商品有多个销售属性如颜色、内存、尺寸时需要自动生成所有可能的SKU组合。以下是Java实现示例public ListSkuDTO generateSkus(SpuDTO spu, ListAttributeDTO saleAttrs) { ListListAttributeValueDTO valueGroups saleAttrs.stream() .map(AttributeDTO::getValues) .collect(Collectors.toList()); ListListAttributeValueDTO combinations new ArrayList(); generateCombinations(valueGroups, 0, new ArrayList(), combinations); return combinations.stream().map(values - { SkuDTO sku new SkuDTO(); sku.setSpuId(spu.getId()); sku.setName(generateSkuName(spu.getName(), values)); sku.setPrice(calculatePrice(spu.getBasePrice(), values)); sku.setAttributeValues(values); return sku; }).collect(Collectors.toList()); } private void generateCombinations(ListListAttributeValueDTO valueGroups, int depth, ListAttributeValueDTO current, ListListAttributeValueDTO result) { if (depth valueGroups.size()) { result.add(new ArrayList(current)); return; } for (AttributeValueDTO value : valueGroups.get(depth)) { current.add(value); generateCombinations(valueGroups, depth 1, current, result); current.remove(current.size() - 1); } }3.2 商品搜索优化设计为支持高效的商品搜索我们需要建立搜索索引表CREATE TABLE product_search_index ( id bigint NOT NULL AUTO_INCREMENT, spu_id bigint NOT NULL, sku_id bigint NOT NULL, title varchar(500) NOT NULL COMMENT 搜索标题, keywords text COMMENT 关键词, attribute_index text COMMENT 属性索引, category_path varchar(100) NOT NULL COMMENT 分类路径, brand_id bigint DEFAULT NULL, price decimal(10,2) NOT NULL, sales int DEFAULT 0, status tinyint NOT NULL DEFAULT 1, PRIMARY KEY (id), FULLTEXT KEY ft_title_keywords (title,keywords), KEY idx_category (category_path), KEY idx_brand (brand_id), KEY idx_price (price), KEY idx_sales (sales) ) ENGINEInnoDB COMMENT商品搜索索引表;3.3 商品上下架状态机设计商品状态管理是电商系统的关键业务逻辑public class ProductStateMachine { private static final MapProductState, SetProductState TRANSITIONS Map.of( ProductState.DRAFT, Set.of(ProductState.PENDING_REVIEW), ProductState.PENDING_REVIEW, Set.of(ProductState.APPROVED, ProductState.REJECTED), ProductState.APPROVED, Set.of(ProductState.LISTED, ProductState.DISCONTINUED), ProductState.LISTED, Set.of(ProductState.UNLISTED, ProductState.DISCONTINUED), ProductState.UNLISTED, Set.of(ProductState.LISTED, ProductState.DISCONTINUED), ProductState.REJECTED, Set.of(ProductState.DRAFT) ); public static void transition(Product product, ProductState newState) { if (!TRANSITIONS.getOrDefault(product.getState(), Set.of()).contains(newState)) { throw new IllegalStateException(Invalid state transition); } product.setState(newState); product.setUpdateTime(LocalDateTime.now()); } }4. 性能优化与扩展设计4.1 读写分离架构对于高流量的电商系统建议采用读写分离架构读操作路径 客户端 → 负载均衡 → 只读实例 → 缓存层(Redis) → 数据库 写操作路径 管理后台 → 主实例 → 数据库 → 同步到只读实例4.2 缓存策略设计商品数据缓存采用多级缓存策略public Product getProductWithCache(Long spuId) { // 一级缓存本地缓存 Product product localCache.get(spuId); if (product ! null) { return product; } // 二级缓存分布式缓存 product redisTemplate.opsForValue().get(buildProductKey(spuId)); if (product ! null) { localCache.put(spuId, product); return product; } // 数据库查询 product productRepository.findById(spuId).orElseThrow(); // 回填缓存 redisTemplate.opsForValue().set(buildProductKey(spuId), product, 30, TimeUnit.MINUTES); localCache.put(spuId, product); return product; }4.3 分库分表策略当商品数据量达到千万级时考虑分库分表-- 按分类ID分片 CREATE TABLE sku_0 ( -- 表结构与sku相同 ) ENGINEInnoDB COMMENTSKU分表0; CREATE TABLE sku_1 ( -- 表结构与sku相同 ) ENGINEInnoDB COMMENTSKU分表1;分片策略建议按分类ID哈希分片热数据如爆款商品单独分片历史数据归档5. 实战中的经验与避坑指南在实际项目中商品中心设计有几个常见陷阱需要特别注意图片存储方案不要将图片直接存储在数据库应使用对象存储服务建立图片元数据表记录图片与商品的关联关系实现图片处理服务缩略图、水印等CREATE TABLE product_image ( id bigint NOT NULL AUTO_INCREMENT, product_id bigint NOT NULL COMMENT SPU或SKU ID, product_type tinyint NOT NULL COMMENT 0-SPU,1-SKU, url varchar(255) NOT NULL COMMENT 图片URL, sort int DEFAULT 0 COMMENT 排序, is_default tinyint DEFAULT 0 COMMENT 是否默认图, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_product (product_id,product_type) ) ENGINEInnoDB COMMENT商品图片表;价格设计的注意事项使用DECIMAL类型存储价格避免浮点数精度问题考虑支持多种货币和汇率转换设计价格历史表记录价格变动CREATE TABLE price_history ( id bigint NOT NULL AUTO_INCREMENT, sku_id bigint NOT NULL, original_price decimal(10,2) NOT NULL COMMENT 原价, sale_price decimal(10,2) NOT NULL COMMENT 售价, start_time datetime NOT NULL COMMENT 生效时间, end_time datetime DEFAULT NULL COMMENT 失效时间, operator varchar(50) DEFAULT NULL COMMENT 操作人, PRIMARY KEY (id), KEY idx_sku (sku_id), KEY idx_time (start_time,end_time) ) ENGINEInnoDB COMMENT价格历史表;库存管理的核心要点实现库存扣减的原子操作处理超卖问题的几种方案对比方案实现方式优点缺点乐观锁使用version字段并发度高需要重试机制悲观锁SELECT FOR UPDATE简单可靠性能影响大Redis原子操作DECR/LUA脚本性能最好需要维护Redis与DB一致性商品数据变更的审计追踪CREATE TABLE product_audit_log ( id bigint NOT NULL AUTO_INCREMENT, product_id bigint NOT NULL COMMENT SPU/SKU ID, product_type tinyint NOT NULL COMMENT 0-SPU,1-SKU, operation varchar(20) NOT NULL COMMENT 操作类型, before_data json DEFAULT NULL COMMENT 变更前数据, after_data json DEFAULT NULL COMMENT 变更后数据, operator varchar(50) DEFAULT NULL COMMENT 操作人, operate_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_product (product_id,product_type), KEY idx_time (operate_time) ) ENGINEInnoDB COMMENT商品操作日志表;

更多文章