商品中都有属性,不同商品,属性往往不同。
在生活中,我们很容易发现,虽然商品不同,规格不同。但是统一分类的商品,比如都是手机,其规格是一致的,但是值不一样。也就是说,商品的规格参数应该是与分类绑定的。每一个分类都有统一的规格参数模板,但不同商品其参数值可能不同。
因此:
规格参数的名称(key)与值(value)应该分开来保存;一个分类,对应一套规格参数模板,只有规格参数key,没有值;一个分类对应多个商品,每个商品的规格值不同,每个商品对应一套规格的值。值我们暂且不管,新增商品时,再来填写规格参数值即可,我们先思考规格参数模板(key)该如何设计。 先看下规格参数的结构:
规格数据首先要分组,组内再有不同的规格参数不同分类,其分组名称也不同不停分类,组内属性也不同 这样就意味着:有多少分类,就有多少分组。 如果按照传统设计,我们会以规格参数作为数据库字段名。如品牌、型号都是字段,那么表的字段就会无限多。这样的表称为横表。一条信息,描述所有数据。例如: 我们不这样做,我们一条信息,只描述一条规格属性,也就是把规格参数作为字段的值,而非字段本身。这样的设计称为竖表设计。例如: 不过,规格和规格组也要单独保存,都采用竖表设计。所以我们有两张表: 如图:规格参数分组表:tb_spec_group
CREATE TABLE `tb_spec_group` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `cid` bigint(20) NOT NULL COMMENT '商品分类id,一个分类下有多个规格组', `name` varchar(50) NOT NULL COMMENT '规格组的名称', PRIMARY KEY (`id`), KEY `key_category` (`cid`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='规格参数的分组表,每个商品分类下有多个规格参数组';规格参数表:tb_spec_param
CREATE TABLE `tb_spec_param` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `cid` bigint(20) NOT NULL COMMENT '商品分类id', `group_id` bigint(20) NOT NULL, `name` varchar(255) NOT NULL COMMENT '参数名', `numeric` tinyint(1) NOT NULL COMMENT '是否是数字类型参数,true或false', `unit` varchar(255) DEFAULT '' COMMENT '数字类型参数的单位,非数字类型可以为空', `generic` tinyint(1) NOT NULL COMMENT '是否是sku通用属性,true或false', `searching` tinyint(1) NOT NULL COMMENT '是否用于搜索过滤,true或false', `segments` varchar(1000) DEFAULT '' COMMENT '数值类型参数,如果需要搜索,则添加分段间隔值,如CPU频率间隔:0.5-1.0', PRIMARY KEY (`id`), KEY `key_group` (`group_id`), KEY `key_category` (`cid`) ) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COMMENT='规格参数组下的参数名';通用属性
用一个布尔类型字段来标记是否为通用:
generic来标记是否为通用属性: true:代表通用属性false:代表sku特有属性搜索过滤
与搜索相关的有两个字段:
searching:标记是否用作过滤 true:用于过滤搜索false:不用于过滤 segments:某些数值类型的参数,在搜索时需要按区间划分,这里提前确定好划分区间 比如电池容量,0 ~ 2000mAh,2000mAh ~ 3000mAh,3000mAh ~ 4000mAh数值类型
某些规格参数可能为数值类型,这样的数据才需要划分区间,我们有两个字段来描述:
numberic:是否为数值类型 true:数值类型false:不是数值类型 unit:参数的单位我们现在要表示规格参数的key,不要值。 如果用java对象表示:首先可以知道其可以分成 组 和 组内属性 两部分。 组 和 组内属性 分别是两类事物。 对组进行描述:对组内的共同特征进行抽取,可以采用java中的 类(class Group) 进行描述。将主体、基本信息、操作系统等看成类中的name字段,将每一个分组下对应的多个参数(主体下的 型号、入网年份)看成类中的List params,同时,每个组也会有一个属于自己的id来标识自己。
对组内属性进行描述 :上述的List params 又是一个新的事物, 同样也可采用 类(class Param) 的概念进行描述。同样有name(参数名)、id字段,同时还要有一个groupId来说明这些参数属于哪个组分类下的,这样就做到了Group表和Param表相互关联。同时,Group表中也应添加categoryId来说明这些规格组是属于哪个商品分类下的。但是,再考虑,规格参数属性里应该也要加入是否为数值类型的字段;若是,那还会衍生出一个新的字段Unit;由于有一些规格参数的字段将来是要做搜索的,因此还应该有判断该参数是否为搜索字段;如果可以搜索,同时又是数值类型,那还会有一个segment字段(字符串类型)。到此为止,规格参数就描述清楚了。
乐优商城是一个全品类的电商网站,因此商品的种类繁多,每一件商品,其属性又有差别。为了更准确描述商品及细分差别,抽象出两个概念:SPU和SKU。
SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集
SKU:Stock Keeping Unit(库存量单位),SPU商品集因具体特性不同而细分的每个商品
以图为例:
本页的 华为Mate10 就是一个商品集(SPU)因为颜色、内存等不同,而细分出不同的Mate10,如亮黑色128G版。(SKU)可以看出:
SPU是一个抽象的商品集概念,为了方便后台的管理SKU才是具体要销售的商品,每一个SKU的价格、库存可能会不一样,用户购买的是SKU而不是SPU同一个SPU下的SKU有共有属性,也有特有属性其实,SKU的特有属性是商品规格参数的一部分。规格参数我们已经提前设计好了,每一个分类都有自己的规格参数。因此我们在设计商品表的时候不要设计特有属性,这些特性设计到规格参数里了。因此规格参数表里的generic字段对 特有属性还是通用属性 进行了区分。 也就是说,我们没必要单独对SKU的特有属性进行设计,它可以看做是规格参数中的一部分。这样规格参数中的属性可以标记成两部分:
所有sku共享的规格属性(称为通用属性),我们记录在spu表中每个sku不同的规格属性(称为特有属性),我们记录在sku表中由于SPU表中某些字段的值非常大,比如商品描述。
因此为了不影响spu表的性能及查询速度,我们对表做了垂直拆分,将SPU的详情放到了另一张表:tb_spu_detail
注:
垂直拆分:表字段过多,实现数据的解耦水平拆分:表数据量多(一张表有1万条数据,水平拆分成两个表,每个表5千条数据)数据库查询时做排序较麻烦,不过现在有很多中间件帮我们实现分表 查询、排序等。用在 订单表中,订单数据量很大,一张表存不下,必然要做水平拆分。前面讲过规格参数与商品分类绑定,一个分类下的所有SPU具有类似的规格参数。SPU下的SKU可能会有不同的规格参数,因此:
SPU中保存通用的规格参数信息。SKU中保存特有规格参数。generic_spec:保存通用规格参数信息的值,这里为了方便查询,使用了json格式。 json结构,其中都是键值对:
key:对应的规格参数的spec_param的idvalue:对应规格参数的值由于SPU下的每一个sku,其值也都不一样,所以值会有很多,形成数组。
因此我们在SPU中,会把特有属性的所有值都记录下来,形成一个数组: 也是json结构:
key:规格参数idvalue:spu属性的数组问题:特有规格参数应该在sku中记录才对,为什么在spu中也要记录一份(聚合sku集合)?
因为我们有时候需要把所有规格参数都查询出来,而不是只查询1个sku的属性。比如,商品详情页展示可选的规格参数时:刚好符号我们的结构,这样页面渲染就非常方便了。
还有一张stock表,代表库存;
因为库存字段写频率较高,而SKU的其它字段(颜色、价格)以读为主,因此我们将两张表分离,读写不会干扰(垂直拆分)。
在SPU表中,已经对特有规格参数及可选项进行了保存,结构如下:
{ "4": [ "香槟金", "樱花粉", "磨砂黑" ], "12": [ "2GB", "3GB" ], "13": [ "16GB", "32GB" ] }这些特有属性如果排列组合,会产生12个不同的SKU,而不同的SKU,其属性就是上面备选项中的一个。
比如:
红米4X,香槟金,2GB内存,16GB存储红米4X,磨砂黑,2GB内存,32GB存储你会发现,每一个属性值,对应于SPUoptions数组的一个选项,如果我们记录下角标,就是这样:
红米4X,0,0,0红米4X,2,0,1既然如此,我们就可以将不同角标串联起来,作为SPU下不同SKU的标示。这就是我们的indexes字段。 这个设计在商品详情页(页面渲染)会特别有用:当用户点击选中一个特有属性,你就能根据 角标快速定位到sku。
看结构:
{ "4":"香槟金", "12":"2GB", "13":"16GB" }保存的是特有属性的键值对。
SPU中保存的是可选项,但不确定具体的值,而SKU中的保存的就是具体的值。