商品在電商領域中是一個非常重要的領域,交易行為前提是有商品資訊存在。本文我們分析商品表基本設計,其它複雜場景可以在此基礎上進行擴充套件。需要說明第一本文所用資料是測試資料,可能與真實資料有偏差,僅供演示。第二本文展示商品核心欄位,一些通用欄位不展示。
類目表示商品分類並且具有層級關係:
後臺類目有兩個特點:標準和穩定。標準表示後臺類目是業界通用的,並且層級不宜過多,通常不超過三級。穩定表示後臺類目一旦確定不能輕易修改,否則設計上下游大量資料變更,工作量非常大,所以變更許可權必須收斂到平臺運營。
計算機領域有一句話:任何問題都可以通過加一層解決。為了解決後臺類目不能靈活調整這個問題,業界在後臺類目上設計了前臺類目。
運營人員通常會對後臺類目進行簡化和整理,更加符合使用者檢索習慣,前臺類目可以自由關聯後臺類目,可以一對多、多對一或者多對多。很多電商網站PC首頁展示的類目一般是前臺類目。
店鋪類目靈活度更好可以交由商家管理,商家可以根據自身經營策略調整店鋪類目,提升交易率,一般只支援兩層。綜上所述我們看一個範例:
CREATE TABLE `category_1_background` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`category_id` varchar(64) NOT NULL COMMENT '類目ID',
`category_name` varchar(128) NOT NULL COMMENT '類目名稱',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_category_id` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='一級後臺類目表';
CREATE TABLE `category_2_background` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`category_id` varchar(64) NOT NULL COMMENT '類目ID',
`category_name` varchar(128) NOT NULL COMMENT '類目名稱',
`category_1_id` varchar(64) NOT NULL COMMENT '一級分類ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_category_id` (`category_id`),
KEY `idx_category_1_id` (`category_1_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='二級後臺類目表';
CREATE TABLE `category_3_background` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`category_id` varchar(64) NOT NULL COMMENT '類目ID',
`category_name` varchar(128) NOT NULL COMMENT '類目名稱',
`category_2_id` varchar(64) NOT NULL COMMENT '二級分類ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_category_id` (`category_id`),
KEY `idx_category_2_id` (`category_2_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='三級後臺類目表';
insert into `category_1_background`(`category_id`,`category_name`) values ('700','電子裝置');
insert into `category_2_background`(`category_id`,`category_name`,`category_1_id`) values ('800','通訊裝置','700');
insert into `category_3_background`(`category_id`,`category_name`,`category_2_id`) values ('900','手機','800');
關鍵屬性是商品本質屬性,關鍵屬性中最核心兩個屬性是品牌和型號,一旦確定這兩個屬性等價於確定SPU(Standard Product Unit)標準化管理單元,例如品牌是小米,型號是10。在實踐中這兩個屬性不放在屬性表:
其它關鍵屬性例如螢幕尺寸,CPU型號,CPU核數還是通過屬性表承載。
銷售屬性又稱為規格屬性,如果關鍵屬性確定,一旦銷售屬性再確定,那麼就可以確定SKU(Stock Keeping Unit)庫存計量單位,可以理解為倉庫中實物商品,每一個SKU都有一個庫存數量與一個價格與之對應。電商常見銷售屬性有顏色、容量、版本、套餐等等。
例如關鍵屬性品牌是小米,型號是10,銷售屬性顏色是黑色和藍色,容量是128G和256G,那麼共有四個SKU:
除了關鍵屬性與銷售屬性,其它屬性稱為描述資訊。
屬性和屬性值由平臺運營人員設定。屬性有兩種型別:選擇與自定義。對於選擇型別,運營人員需要為屬性設定屬性值。對於自定義型別,無需設定屬性值。例如平臺運營人員新增以下兩個屬性:
每個類目對應的屬性是不同的,所以平臺運營人員初始化屬性和屬性值之後,還要建立類目與屬性關聯關係。因為同一個屬性對於不同類目重要性不同,所以在設定類目和屬性關係時需要設定以下資訊:
屬性還有繼承關係,平臺運營人員不僅可以為三級類目設定屬性,還可以為一級和二級類目設定屬性。例如運營人員為二級類目設定A、B兩種屬性,那麼這個二級類目下三級類目同時也具有A、B兩種屬性,類目與屬性關係如下圖:
CREATE TABLE `attribute` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`attribute_id` varchar(64) NOT NULL COMMENT '屬性ID',
`biz_type` tinyint(1) NOT NULL COMMENT '1選擇 2自定義',
`attribute_name` varchar(128) NOT NULL COMMENT '屬性名稱',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_attribute_id` (`attribute_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='屬性表';
insert into `attribute` (`attribute_id`, `biz_type`, `attribute_name`) values('100','1','顏色');
insert into `attribute` (`attribute_id`, `biz_type`, `attribute_name`) values('200','1','儲存容量');
insert into `attribute` (`attribute_id`, `biz_type`, `attribute_name`) values('300','2','重量');
insert into `attribute` (`attribute_id`, `biz_type`, `attribute_name`) values('400','2','螢幕尺寸');
CREATE TABLE `attribute_value` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`attribute_value_id` varchar(64) NOT NULL COMMENT '屬性值ID',
`attribute_value` varchar(128) NOT NULL COMMENT '屬性值名稱',
`attribute_id` varchar(64) NOT NULL COMMENT '屬性ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_attribute_value_id` (`attribute_value_id`),
KEY `idx_attribute_id` (`attribute_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='屬性值表';
--顏色屬性值
insert into `attribute_value`(`attribute_value_id`,`attribute_value`,`attribute_id`) values ('1001','藍色','100');
insert into `attribute_value`(`attribute_value_id`,`attribute_value`,`attribute_id`) values ('1002','黑色','100');
insert into `attribute_value`(`attribute_value_id`,`attribute_value`,`attribute_id`) values ('1003','紅色','100');
--容量屬性值
insert into `attribute_value`(`attribute_value_id`,`attribute_value`,`attribute_id`) values ('2001','64G','200');
insert into `attribute_value`(`attribute_value_id`,`attribute_value`,`attribute_id`) values ('2002','128G','200');
insert into `attribute_value`(`attribute_value_id`,`attribute_value`,`attribute_id`) values ('2003','256G','200');
CREATE TABLE `relation_category_attribute` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`relation_id` varchar(64) NOT NULL COMMENT '關係ID',
`attribute_id` varchar(64) NOT NULL COMMENT '屬性ID',
`category_id` varchar(64) NOT NULL COMMENT '類目ID',
`category_level` tinyint(1) NOT NULL COMMENT '類目層級',
`attribute_type` tinyint(1) NOT NULL COMMENT '屬性型別 1關鍵屬性 2銷售屬性 3描述屬性',
`must_fill` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否必填',
`product_scope` tinyint(1) NOT NULL COMMENT '商品維度 1spu 2sku',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_relation_id` (`relation_id`),
KEY `idx_category_id_attribute_id` (`category_id`,`attribute_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='屬性與類目關係表';
insert into `relation_category_attribute`(`relation_id`,`attribute_id`,`category_id`,`category_level`,`attribute_type`,`must_fill`,`product_scope`) values ('10000000','100','900',3,2,1,2);
insert into `relation_category_attribute`(`relation_id`,`attribute_id`,`category_id`,`category_level`,`attribute_type`,`must_fill`,`product_scope`) values ('10000001','200','900',3,2,1,2);
insert into `relation_category_attribute`(`relation_id`,`attribute_id`,`category_id`,`category_level`,`attribute_type`,`must_fill`,`product_scope`) values ('10000002','300','900',3,3,1,2);
insert into `relation_category_attribute`(`relation_id`,`attribute_id`,`category_id`,`category_level`,`attribute_type`,`must_fill`,`product_scope`) values ('10000003','400','900',3,1,1,1);
CREATE TABLE `product_brand` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`brand_id` varchar(128) NOT NULL COMMENT '品牌ID',
`brand_cn_name` varchar(128) NOT NULL COMMENT '品牌中文名',
`brand_en_name` varchar(128) NOT NULL COMMENT '品牌英文名',
`logo_url` text COMMENT '品牌Logo',
`brand_story` text COMMENT '品牌故事',
PRIMARY KEY (`id`),
KEY `idx_brand_id` (`brand_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='品牌表';
insert into `product_brand`(`brand_id`,`brand_cn_name`,`brand_en_name`) values ('1000','小米','MI');
第三章節屬性相關資訊由平臺運營人員設定,為商家維護商品資訊客製化一個規範,這時商家可以根據設定自己的商品資訊。
我們還是看一個手機範例:
CREATE TABLE `product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`product_id` varchar(64) NOT NULL COMMENT '商品id',
`shop_id` varchar(64) NOT NULL COMMENT '店鋪id',
`brand_id` bigint(20) NOT NULL COMMENT '品牌id',
`product_model` varchar(256) NOT NULL COMMENT '商品型號',
`product_name` varchar(256) NOT NULL COMMENT '商品名稱',
`sale_status` tinyint(1) NOT NULL COMMENT '銷售狀態 1上架 2下架',
`category_3_id` varchar(64) NOT NULL COMMENT '三級分類id',
`img_url` text COMMENT '圖片路徑',
`description` text COMMENT '商品描述',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_product_id` (`product_id`),
KEY `idx_brand_id` (`brand_id`),
KEY `idx_category_3_id` (`category_3_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='SPU';
insert into product(`product_id`, `shop_id`, `brand_id`, `product_model`, `product_name`, `category_3_id`, `sale_status`) values ('100', 'shop_1','100', '10', '小米10手機', '900', 1);
CREATE TABLE `relation_product_attribute` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`relation_id` varchar(64) NOT NULL COMMENT '關係ID',
`product_id` varchar(64) DEFAULT NULL COMMENT '商品ID',
`attribute_id` varchar(64) NOT NULL COMMENT '屬性ID',
`attribute_value_id` varchar(64) DEFAULT NULL COMMENT '屬性值ID',
`custom_attribute_value` varchar(256) DEFAULT NULL COMMENT '自定義屬性值',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_relation_id` (`relation_id`),
KEY `idx_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='SPU與屬性關係表';
insert into relation_product_attribute(`relation_id`, `product_id`, `attribute_id`, `attribute_value_id`, `custom_attribute_value`) values ('20000000', '100', '400', NULL, '6.67英寸');
CREATE TABLE `product_sku` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`sku_id` VARCHAR(64) NOT NULL COMMENT 'skuId',
`sku_name` VARCHAR(128) NOT NULL COMMENT 'sku名稱',
`product_id` VARCHAR(64) NOT NULL COMMENT '商品id',
`sale_status` tinyint(1) NOT NULL COMMENT '銷售狀態 1上架 2下架',
`orgin_price` DECIMAL(10,2) NOT NULL COMMENT '原價',
`discount_price` DECIMAL(10,2) NOT NULL COMMENT '優惠價格',
`stock_count` INT(11) NOT NULL COMMENT '剩餘庫存',
`lock_stock_count` INT(11) NOT NULL COMMENT '鎖定庫存',
`sale_stock_count` INT(11) NOT NULL COMMENT '銷售量',
`sku_img_url` TEXT COMMENT '圖片路徑',
`sku_description` TEXT COMMENT '商品描述',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_sku_id` (`sku_id`),
KEY `idx_product_id` (`product_id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='SKU';
insert into `product_sku`(`sku_id`,`sku_name`,`product_id`,`orgin_price`,`discount_price`,`stock_count`,`lock_stock_count`,`sale_stock_count`,`sku_img_url`,`sku_description`, `sale_status`) values ('200','小米 10 藍色 128G', '100',3000,3000,3,1,1000,NULL,NULL, 1);
CREATE TABLE `relation_product_sku_attribute` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`relation_id` varchar(64) NOT NULL COMMENT '關係ID',
`sku_id` varchar(64) DEFAULT NULL COMMENT 'skuId',
`attribute_id` varchar(64) NOT NULL COMMENT '屬性ID',
`attribute_value_id` varchar(64) DEFAULT NULL COMMENT '屬性值ID',
`custom_attribute_value` varchar(256) DEFAULT NULL COMMENT '自定義屬性值',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_relation_id` (`relation_id`),
KEY `idx_sku_id` (`sku_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='SKU與屬性關係表';
insert into relation_product_sku_attribute(`relation_id`, `sku_id`, `attribute_id`, `attribute_value_id`, `custom_attribute_value`) values ('30000000', '200', '100', '1001', NULL);
insert into relation_product_sku_attribute(`relation_id`, `sku_id`, `attribute_id`, `attribute_value_id`, `custom_attribute_value`) values ('30000001', '200', '200', '2002', NULL);
insert into relation_product_sku_attribute(`relation_id`, `sku_id`, `attribute_id`, `attribute_value_id`, `custom_attribute_value`) values ('30000002', '200', '300', NULL, '173克');
CREATE TABLE `product_sku_sn` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`sn_id` varchar(64) NOT NULL COMMENT '序列號ID',
`sku_id` varchar(64) DEFAULT NULL COMMENT 'skuId',
`sn` varchar(64) DEFAULT NULL COMMENT '序列號',
`status` tinyint(1) NOT NULL COMMENT '狀態 1未售 2已售',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_sn_id` (`sn_id`),
UNIQUE KEY `uq_sn_sku` (`sku_id`,`sn`),
KEY `idx_sku_id` (`sku_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='SN';
insert into product_sku_sn(`sn_id`, `sku_id`, `sn`, `status`) values ('40000000', '200', 'SN-1', 1);
insert into product_sku_sn(`sn_id`, `sku_id`, `sn`, `status`) values ('40000001', '200', 'SN-2', 1);
insert into product_sku_sn(`sn_id`, `sku_id`, `sn`, `status`) values ('40000002', '200', 'SN-3', 1);
商品操作總體上分為平臺運營和商家兩個角色,平臺運營主要維護類目、屬性、類目與屬性關係資訊,相當於為商家維護商品設定規範。商家主要維護spu、sku、spu具體屬性值是什麼、sku具體屬性值是什麼、上下架狀態。
以商品表為例,我們不應該以id
作為商品Id,應該設定product_id
作為商品Id,要求product_id
全域性唯一,這樣便於當資料量過大時進行分庫分表。
商品表是一個高讀寫比典型,可能看10次才會產生1次購買行為,所以如果可以一次查詢就查出所需資訊對效能會更友好。
本文表設計並沒有進行冗餘,例如如果要冗餘可以在relation_product_sku_attribute
表中新增attribute_value
欄位記錄屬性值。冗餘問題就是資料一致性,例如當屬性值發生變化時,上述欄位也要同步進行修改。
正如上述章節所述,商品表是一個高讀寫比的典型,我們希望一次查詢可以將所需資訊查詢出來,而不是跨多張表去查詢,但是我們又不想在業務表冗餘資料。
我們可以將商品資訊平鋪到ES中一個索引,這個索引具有商品全部欄位資訊,例如在查詢商品列表或者商詳時可以直接存取這個索引。
product_sku
有庫存欄位,對於庫存欄位修改相對而言比較多,其它資訊變更比較少,因為如果出現下單、購買、退款行為,庫存資訊就會發生變化。所以可以將sku主表庫存欄位單獨成表,從而減輕主表壓力。
本文通過介紹類目、屬性、品牌、SPU、SKU、SN引出商品十二張基礎表:
一級類目表:category_1_background
二級類目表:category_2_background
三級類目表:category_3_background
屬性表:attribute
屬性值表:attribute_value
類目與屬性關係表:relation_category_attribute
品牌表:product_brand
SPU表:product
SKU表:product_sku
SN表:product_sku_sn
SPU與屬性關係表:relation_product_attribute
SKU與屬性關係表:relation_product_sku_attribute
這些十二張基礎表從不同側面描述了商品資訊,其它複雜場景可以在這些基礎表上進行擴充套件。同時在第五章節我們討論了五個延伸知識,希望本文對大家有所幫助。