大型商城:商城业务-商品上架

ES储存分析

需要存储那些信息到ES中去

  1. 需要保存sku信息
  • 当搜索商品名的时候,检索到的是sku的sku_title
  • 还会通过sku的销量、标题、价格区间来进行检索
  1. 需要保存品牌分类等信息
  • 点击分类,检索分类下的所有信息
  • 点击品牌,检索品牌下的商品信息
  1. 需要保存spu的规格信息——选择规格的时候会检索共有这些规格的spu信息

如何设计存储结构 商品Mapping

1.将sku的全量信息保存进去,包括spu的属性  造成空间冗余

// 优点:方便检索
// 缺点:会产生冗余字段,对于属于同一spu的商品,attrs字段数据会冗余

举例

{
    skuId:1
    spuId:11
    skuTitle:"华为xx"
    price:998
    saleCount:99
    attrs:[
          {尺寸:"5寸"},
          {CPU:高通945},
          {分辨率:全高清}
    ]
}

2.分拆存储,spu 和 attr 一个索引,sku 单独一个索引可能涉及的问题。

// 优点:空间利用率高
// 缺点:查询attr时,会检索当前所有的spu,耗时很长

举例

建一个sku索引,只保存非冗余字段
{
    skuId:1
    spuId:11
    xxxx
}
再建一个attr索引
{
    spuId:11
    attrs:[
          {尺寸:"5寸"},
          {CPU:高通945},
          {分辨率:全高清}
    ]
}

假设搜索小米,  粮食、手机、电器等有很多sku都包含有小米
10000个包含小米的sku,涉及到4000个spu,我们会找到4000个spu的attr进行聚合,通过分步查询:
1. 查出包含小米的10000个sku,其涉及到4000个spu
2. 查出4000个spu对应的所有可能属性 esClient:spuId:[4000个spuId]  4000*8=32000byte=32kb
32kb*10000=320mb; 如果是百万并发会发送32GB的数据

检索商品的名字,如“手机”,对应的 spu 有很多,我们要分析出这些 spu 的所有关联属性,再做一次查询,就必须将所有 spu_id 都发出去。假设有 1 万个数据,数据传输一次就10000*4=4MB;并发情况下假设 1000 检索请求,那就是 4GB 的数据,传输阻塞时间会很长,业务更加无法继续。

3.如下设计,这样才是文档区别于关系型数据库的地方,宽表设计,不能去考虑数据库范式。

最终方案

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" },
      "hasStock": { "type": "boolean" },
      "hotScore": { "type": "long"  },
      "brandId":  { "type": "long" },
      "catalogId": { "type": "long"  },
      "brandName": {  
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "brandImg":{    # 品牌图片,只用来查看,不用来检索和聚合
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "catalogName": {
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "attrs": {  # 这里是spu的规格属性
        "type": "nested",  # 嵌入式,内部属性
        "properties": {
          "attrId": {"type": "long"  },
          "attrName": {
            "type": "keyword",
            "index": false,
            "doc_values": false
          },
          "attrValue": { "type": "keyword" }
        }
      }
    }
  }
}

关于nested类型

nested官方文档

将user的type修改为 nested ,不进行扁平化处理,就不会发生以上情况!

PUT my_index/_mapping
{
  "mapping":{
    "properties":{
      "user":{
        "type":"nested"
      }
    }
  }
}

 商品上架功能

点击商家把相关信息保存到es中,再修改商品状态

详细代码:com.tinstu.gulimall.product.service.impl.SpuInfoServiceImpl  中的up方法!

feign的调用流程

         /**
             * Feign 调用流程
             * 1.构造请求数据,将对象转换为json
             *       RequestTemplate template = buildTemplateFromArgs.create(argv);
             * 2.发送请求进行执行(执行成功会解码响应数据)
             *       executeAndDecode(template);
             * 3.执行请求会有重试机制
             *      while(true){
             *         try{
             *             executeAndDecode(template);
             *         }catch(){
             *            try{
             *              retryer.continueOrPropagate(e);
             *            } catch(){
             *                throw ex;
             *            }
             *            comtinue;
             *         }
             *      }
             */

 

 

 

阅读剩余
THE END