Elasticsearch实战—ES数据建模与Mysql对比实现 一对多模型
文章目录
- Elasticsearch实战---ES数据建模与Mysql对比实现 一对多模型
- 1.一对多 模型
- 1.1 Mysql建模
- 2.一对多 Index ES 数据模型
- 2.1 类似Mysql, 依旧创建两个Index索引库
- 2.2 采用ES架构 嵌套数组模型
- 2.3采用ES架构 冗余部分字段Object对象模型
- 3.冗余ES字段Object对象模型实战
- 4.冗余ES字段Object对象模型缺陷
- 4.1 演示Object对象缺陷
- 5. 解决办法
我们如何把Mysql的模型合理的在ES中去实现? 就需要你对要存储的数据足够的了解,及对应用场景足够的深入分析,才能建立一个合适的模型,便于你后期扩展
- 一对一 模型
- 一对多 模型
- 多对多 模型
1.一对多 模型
我们现在有两个模型, 一个商品Product, 一个分类Category , 我们对比下一对多模型如何处理 ,一个分类下有多个商品, 商品对分类 1:N 1对多关系
1.1 Mysql建模
对于一对多的数据模型, mysql 可以用2个表 来在Mysql中实现 一对多,通过关联外键主键等, 查 produce_id的同时, 再根据 attribute_id属性id去 关联 查属性表查出商品和分类的关系
手机是一个分类, 但是 手机下面分为 华为手机, 小米手机, VIVO手机等 一对多模型
#关联查询
select * from category left join product on category.category_id = product.category_id;
Table :category 手机分类表
字段 | 类型 |
---|---|
id | 唯一主键,自增 |
category_id | 分类id |
category_name | 分类名称 |
category_remark | 分类标签 |
Table :product 商品表 基本信息 包含分类 id 用于关联
字段 | 类型 |
---|---|
id | 唯一主键,自增 |
product_id | 商品id |
category_id | 分类id |
product_name | 商品名称 |
product_price | 商品价格 |
product_number | 商品数量 |
2.一对多 Index ES 数据模型
对于ES 这种1:N的, 如果我们的场景是查出 一个分类下面的所有商品, 我们应该如何建模?
三种方式
2.1 类似Mysql, 依旧创建两个Index索引库
一个库存 category, 一个存product, 想要查一个分类下面的所有商品 分2步, 先查分类,再根据category_id去 商品库中查所有的商品
优点: 设计简单,参照Mysql,依旧没有冗余数据
缺点: 查询逻辑复杂, 需要多次调用不同索引库的Index去查询API,效率低下
这种方式我们不推荐使用,这里就不再给出 Index mapping结构
2.2 采用ES架构 嵌套数组模型
既然一个分类下多个商品,那我就按照 { 分类1:[商品1, 商品2, 商品3, …, 商品X] } 把所有的商品存储在 一个分类下的数组结构中, 乍一看挺好的,一次性能取出来所有数据
优点: 依旧没有冗余数据, 更新的时候只更新1条数据
缺点: ES的更新document是把原来的记录删掉,重新再插入的机制,如果更新一个分类的名称如果按照分库结构, 只需要更细一个index库的 一条数据就即可, 但是采用ES嵌套数组就要把下面的所有的商品全都查出来,然后更新完他的分类字段后,再全部插入,等同于该分类的所有商品全部更新一遍, 操作数据量大,而且更新不方便
这种方式我们同样不推荐使用, 这里不再给出 Index mapping结构
2.3采用ES架构 冗余部分字段Object对象模型
既然嵌套数组模型不太符合,那我采用冗余方式, 我现在把所有的分类全都存一份 存到商品信息中, 商品信息式最小粒度{ {商品1: 属性, 分类信息1}, {商品2: 属性, 分类信息1}, {商品3: 属性, 分类信息2}, {商品4: 属性, 分类信息2} }, 以冗余分类数据到商品信息中来实现关联
优点: 更新一个商品,不会再更新所有的商品,商品粒度最小
缺点: 更新一个分类, 更新多条数据, 就要更新所有商品的分类的数据, 全部都需要更新, 有多少个此分类的商品,就要更新多少条
- 操作简单, 一次性就能搜索出所有的结果字段
3.冗余ES字段Object对象模型实战
我们采用大致类似与 这样的 Mysql Table :produce 商品表 基本信息
字段 | 类型 |
---|---|
id | 唯一主键,自增 |
product_id | 商品id |
product_name | 商品名称 |
product_price | 商品价格 |
product_number | 商品数量 |
category_name | 分类名称 |
category_remark | 分类标签 |
下面创建Index mapping结构, 我们把多个手机相同的分类信息,作为冗余字段 冗余到 手机基本信息中
索引库结构
PUT /phone_index
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"productId": {
"type": "long"
},
"productName": {
"type": "keyword"
},
"productPrice": {
"type": "long"
},
"productNumber": {
"type": "long"
},
"category": {
"properties": {
"categoryName": {
"type": "keyword"
},
"categoryRemark": {
"type": "keyword"
}
}
}
}
}
}
下面我们给 phone_index 索引库插入数据, 插入 6条手机信息
put /phone_index/_bulk
{"index":{"_id":1}}
{"productId":1,"productName":"P20","productPrice":4000,"productNumber":50,"category":{"categoryName":"华为手机","categoryRemark":"高端"}}
{"index":{"_id":2}}
{"productId":2,"productName":"Honor30","productPrice":2000,"productNumber":100,"category":[{"categoryName":"华为手机","categoryRemark":"很好"},{"categoryName":"荣耀手机","categoryRemark":"便宜"}]}
{"index":{"_id":3}}
{"productId":3,"productName":"小米8","productPrice":2000,"productNumber":600,"category":{"categoryName":"小米手机","categoryRemark":"中端"}}
{"index":{"_id":4}}
{"productId":4,"productName":"红米10","productPrice":2500,"productNumber":300,"category":{"categoryName":"小米手机","categoryRemark":"发烧"}}
{"index":{"_id":5}}
{"productId":5,"productName":"小米Max","productPrice":4000,"productNumber":800,"category":{"categoryName":"小米手机","categoryRemark":"很好"}}
4.冗余ES字段Object对象模型缺陷
上面的结构似乎看起来很合理,而且能解决一部分问题,但是这种对象结构是存在很大缺陷的,为什么 ?
是因为底层ES在存储对象结构的时候都是以数组的形式存储, 比如这个Honor30 手机牌子 ,底层数据存储的就是
- categoryName:[华为i手机, 荣耀手机]
- categoryRemark[很好, 便宜]
- 这里已经失去了绑定关系了, 比如 华为手机-很好, 荣耀手机-便宜 这种对应关系
- 查询的时候就会出现 华为手机-便宜的 这种数据出现
4.1 演示Object对象缺陷
我们要查询 华为手机 便宜的 标签,must 查询, 分类:华为手机,描述:便宜
按照存储的数据, 这种数据应该不存在
get /phone_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"category.categoryName": "华为手机"
}
},
{
"match": {
"category.categoryRemark": "便宜"
}
}
]
}
}
}
查询结果 不是我们想要的, 是错误的
或者 我们再查询以下 华为手机-发烧的 场景, 按照我们的数据, 不存在任何数据把华为手机和发烧关联
must查询, 分类:华为手机, 标签:发烧
get /phone_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"category.categoryName": "华为手机"
}
},
{
"match": {
"category.categoryRemark": "发烧"
}
}
]
}
}
}
查询结果错误, 要查询 华为手机-发烧的数据,结果把 小米手机查询出来了,这是明显的错误
5. 解决办法
Object对象存储会出现上面的问题, 为了解决这种问题,我们要采用Nest结构来存储数据, 这种Nest结构底层不是数组存储的,就不会出现上面的情况
下一篇 我们来介绍Nest结构 解决对象冗余存储Object对象的缺陷