在决定哪些信息应该存储到es之前,首先要明确一点,es中的数据是存储到内存中的,虽然它原生支持分布式,理论上容量无限,但是毕竟内存的价格要比硬盘的价格贵的多,所以尽量能节省的就节省。
就是只保存有用的数据,没用的数据全部不保存,要用的时候,大不了检索出来,去数据库再查一遍。
{
skuId:1
spuId:11
skyTitile:华为xx
price:999
saleCount:99
attrs:[
{尺寸:5寸},
{CPU:高通845},
{分辨率:全高清}
]
}
好处:方便检索
缺点:会产生冗余字段,就会产生对于相同类型的商品,attrs 属性字段会重复,空间占用大
假设有100w条数据,每条数据有20个属性,一共占用2KB的空间, 那100W条数据也才占用2G的空间,其实就是多加一根内存条,即使占用20G,也不过是加多少内存的问题
sku索引{
skuId:1
spuId:11
sku其它的基本信息...
}
attr索引{
spuId:11
attrs:[
{尺寸:5寸},
{CPU:高通845},
{分辨率:全高清}
]
}
将数据分到两个索引下,这样就不会产生数据冗余的问题
那么如何查找attr属性呢?
每一个sku索引里面,都有spuId,获取到这个spuId之后,再去attr索引里面,根据spuId查找即可
假如搜索“手机”,系统会找到标题里面包含手机的这些商品,并且它会把这些商品聚合起来,分析这些商品所涉及的所有属性,以及所有的属性值,然后再展示到页面,这样就会保证,点进某一个属性值,下面一定有商品,所以检索条件是动态计算的。
假设搜索“小米”,会发现它存在于好几个分类中,比如:粮食、手机、电器等等,会有很多sku都包含“小米”
如果要检索出这些sku所包含的所有属性怎么办
假设标题带有“小米”两个字的商品有1w个,这1w个商品涉及到4000个spu,我们就需要找到这4000个spu所对应的属性,来进行聚合,我们会做分步查询
这里一分步,就相当于会调用第二次查询 ,第二次,es的客户端会给es发送请求,这个请求不说别的,就说spuId这个数组,里面就需要传4000个spuId,这4000个spuId因为都是Long类型,假设每个都占8字节,4000*8,那就是32000个字节,等与32kb
这么1次请求,就需要发送32kb的数据,假设现在有1w人正在商城检索商品,集群中光数据传输,就会产生320MB,
如果真是百万并发,点个检索,就会传出32GB的数据,这32GB的数据,光网络阻塞,时间可能就会非常长
这种方式看着可以,但是随着系统的不断壮大,未来可能就会产生上述的问题。
完整结构(修改了部分字段,请按需选择):
PUT product
{
"mappings": {
"properties": {
"skuId": {
"type": "long" },
"spuId": {
"type": "keyword" },
"skuTitle": {
"type": "text",
"analyzer": "ik_smart"
},
"skuPrice": {
"type": "keyword" },
"skuImg": {
"type": "keyword",
"index": false,
"doc_values": false
},
"saleCount":{
"type":"long" },
"stockOrNot": {
"type": "boolean" },
"hotScore": {
"type": "long" },
"brandId": {
"type": "long" },
"categoryId": {
"type": "long" },
"brandName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"brandImg":{
"type": "keyword",
"index": false,
"doc_values": false
},
"categoryName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long" },
"attrName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"attrValue": {
"type": "keyword" }
}
}
}
}
}
"mappings": {
"properties": {
"skuId": {
"type": "long" },
"spuId": {
"type": "keyword" }, # 精确检索,不分词
"skuTitle": {
"type": "text", # 全文检索
"analyzer": "ik_smart" # 分词器
},
"skuPrice": {
"type": "keyword" }, # 为了避免精度问题,使用keyword
"skuImg": {
"type": "keyword",
"index": false, # false 不可被检索,冗余字段都加上
"doc_values": false # false 不可被聚合,冗余字段都加上
},
"saleCount":{
"type":"long" }, # 商品销量
# 商品是否有库存
# 可以避免每天都跟数据库核查数量,因为数据只要已修改,es就会重新把它索引一次,
# 维护整片索引是一个很慢的操作,所以只在商品没有库存的时候,才将其改为false,
# 上了库存,再将其改为true,这样会比实时更新库存好的多
"hasStock": {
"type": "boolean" },
"hotScore": {
"type": "long" }, # 商品热度评分
"brandId": {
"type": "long" }, # 品牌id
"categoryId": {
"type": "long" }, # 分类id
"brandName": {
# 品牌名,
"type": "keyword",
"index": false,
"doc_values": false
},
"brandImg":{
# 品牌图片
"type": "keyword",
"index": false,
"doc_values": false
},
"categoryName": {
# 分类名
"type": "keyword",
"index": false,
"doc_values": false
},
"attrs": {
# 当前这个sku所拥有的所有属性规格
"type": "nested", # 嵌入式,内部属性
"properties": {
"attrId": {
"type": "long" },
"attrName": {
# 属性名
"type": "keyword",
"index": false,
"doc_values": false
},
"attrValue": {
"type": "keyword" } # 属性值
}
}
}
}