Elasticsearch之join关联查询及使用场景

在Elasticsearch这样的分布式系统中执行类似SQL的join连接是代价是比较大的,然而,Elasticsearch却给我们提供了基于水平扩展的两种连接形式 。这句话摘自Elasticsearch官网,从“然而”来看,说明某些场景某些情况下我们还是可以使用的A84致力于为用户收集丰富的生活经验知识

一、join总述

1、关系类比

在关系型数据库中,以MySQL为例,尤其B端类系统且数据量不是特别大的场景,我们经常用到join关键字对有关系的两张或者多张表进行关联查询。但是当数据量达到一定量级时,查询性能就是经常困扰的问题。由于es可以做到数亿量级的秒查(具体由分片数量决定),这时候把数据同步到es是我们可以使用解决方案之一。A84致力于为用户收集丰富的生活经验知识

那么不禁有疑问问了,由于业务场景的决定,之前必须关联查询的两张表还能做到进行关联吗?A84致力于为用户收集丰富的生活经验知识

答案是可以的,es也提供了类似于关系型数据库的关联查询,但是它又与关系型数据的关联查询有明显的区别与限制。A84致力于为用户收集丰富的生活经验知识

2、使用场景

如果把关系数据库原有关联的两张表,同步到es后,通常情况下,我们业务开发中会有两种查询诉求的场景A84致力于为用户收集丰富的生活经验知识

场景1A84致力于为用户收集丰富的生活经验知识

诉求:展示子表维度的明细数据(包含父表和子表中字段的条件)A84致力于为用户收集丰富的生活经验知识

方案:对于此种查询诉求,我们可以把原来关联的父子表打成父子表字段混合在一起的大宽表,既能满足查询条件,又有查询性能的保障,也是常用存储方案之一A84致力于为用户收集丰富的生活经验知识

场景2A84致力于为用户收集丰富的生活经验知识

诉求:展示父表维度的明细数据(包含父表和子表中字段的条件)A84致力于为用户收集丰富的生活经验知识

方案:然而,对于此种查询诉求,需要通过子表的条件来查询出父表的明细结果,场景1的宽表存储方案是子表明细数据,而最终我们要的是父表明细数据,显然对于场景1的存储方案是不能满足的。如果非要使用场景1的存储方案,我们还要对宽表结果进行一次groupby或者collapse操作来得到父表结果。A84致力于为用户收集丰富的生活经验知识

这个时候我们就可以使用es提供的join功能来完成场景2的诉求查询,同时它也满足场景1的诉求查询A84致力于为用户收集丰富的生活经验知识

3、使用限制

由于es属于分布式文档型数据库,数据自然是存在于多个分片之上的。Join字段自然不能像关系型数据库中的join使用。在es中为了保证良好的查询性能,最佳的实践是将数据模型设置为非规范化文档,通过字段冗余构造宽表,即存储在一个索引中。需要满足条件如下:A84致力于为用户收集丰富的生活经验知识

(1)父子文档(数据)必须存储在同一index中A84致力于为用户收集丰富的生活经验知识

(2)父子文档(数据)必须存储在同一个分片中,通过关联父文档ID关联A84致力于为用户收集丰富的生活经验知识

(3)一个index中只能包含一个join字段,但是可以有多个关系A84致力于为用户收集丰富的生活经验知识

(4)同一个index中,一个父关系可以对应多个子关系,一个子关系只对应一个父关系A84致力于为用户收集丰富的生活经验知识

4、性能问题

当然执行了join查询固然性能会受到一定程度的影响。对于带has_child/has_parent而言,其查询性能会随着指向唯一父文档的匹配子文档的数量增加而降低。本文开篇第一句摘自es官网描述,从ES官方的描述来看join关联查询对性能的损耗是比较大的。A84致力于为用户收集丰富的生活经验知识

不过,在笔者使用的过程中,在5个分片的前提下,且父表十万量级,子表数据量在千万量级的情况下,关联查询的耗时还是在100ms内完成的,对于B端许多场景还是可以接受的。A84致力于为用户收集丰富的生活经验知识

若有类似场景,建议我们在使用前,根据分片的多少和预估未来数据量的大小提前做好性能测试,防止以后数量达到一定程度时,性能有明显下降,那个时候再改存储方案得不偿失。A84致力于为用户收集丰富的生活经验知识

二、Mapping

1、举例说明

这里以优惠券活动与优惠券明细为例,在一个优惠券活动中可以发放几千万的优惠券,所以券活动与券明细是一对多的关系。A84致力于为用户收集丰富的生活经验知识

券活动表字段A84致力于为用户收集丰富的生活经验知识

字段A84致力于为用户收集丰富的生活经验知识

说明A84致力于为用户收集丰富的生活经验知识

activity_idA84致力于为用户收集丰富的生活经验知识

活动IDA84致力于为用户收集丰富的生活经验知识

activity_nameA84致力于为用户收集丰富的生活经验知识

活动名称A84致力于为用户收集丰富的生活经验知识

券明细表字段A84致力于为用户收集丰富的生活经验知识

字段A84致力于为用户收集丰富的生活经验知识

说明A84致力于为用户收集丰富的生活经验知识

coupon_idA84致力于为用户收集丰富的生活经验知识

券IDA84致力于为用户收集丰富的生活经验知识

coupon_amountA84致力于为用户收集丰富的生活经验知识

券面额A84致力于为用户收集丰富的生活经验知识

activity_idA84致力于为用户收集丰富的生活经验知识

外键-活动IDA84致力于为用户收集丰富的生活经验知识

2、mapping释义

join类型的字段主要用来在同一个索引中构建父子关联关系。通过relations定义一组父子关系,每个关系都包含一个父级关系名称和一个或多个子级关系名称

activity_coupon_field是一个关联字段,内部定义了一组join关系,该字段为自命名A84致力于为用户收集丰富的生活经验知识

type指定关联关系是join,固定写法A84致力于为用户收集丰富的生活经验知识

relations定义父子关系,activity父类型名称,coupon子类型名称,名称均为自命名A84致力于为用户收集丰富的生活经验知识

{A84致力于为用户收集丰富的生活经验知识

"mappings": {A84致力于为用户收集丰富的生活经验知识

"properties": {A84致力于为用户收集丰富的生活经验知识

"activity_coupon_field": {A84致力于为用户收集丰富的生活经验知识

"type": "join",A84致力于为用户收集丰富的生活经验知识

"relations": {A84致力于为用户收集丰富的生活经验知识

"activity": "coupon"A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

},A84致力于为用户收集丰富的生活经验知识

"activity_id": {A84致力于为用户收集丰富的生活经验知识

"type": "keyword"A84致力于为用户收集丰富的生活经验知识

},A84致力于为用户收集丰富的生活经验知识

"activity_name": {A84致力于为用户收集丰富的生活经验知识

"type": "keyword"A84致力于为用户收集丰富的生活经验知识

},A84致力于为用户收集丰富的生活经验知识

"coupon_id": {A84致力于为用户收集丰富的生活经验知识

"type": "long"A84致力于为用户收集丰富的生活经验知识

},A84致力于为用户收集丰富的生活经验知识

"coupon_amount": {A84致力于为用户收集丰富的生活经验知识

"type": "long"A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

三、插入数据

1、插入父文档

在put父文档数据的时候,我们通常按照某种规则指定文档ID,方便子文档数据变更时易于得到父文档ID。比如这里我们用activity_id的值:activity_100来作为父idA84致力于为用户收集丰富的生活经验知识

PUT /coupon/_doc/activity_100A84致力于为用户收集丰富的生活经验知识

{A84致力于为用户收集丰富的生活经验知识

"activity_id": 100,A84致力于为用户收集丰富的生活经验知识

"activity_name": "年货节5元促销优惠券",A84致力于为用户收集丰富的生活经验知识

"activity_coupon_field": {A84致力于为用户收集丰富的生活经验知识

"name": "activity"A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

2、插入子文档

上边已经指定了父文档ID,而子表中已经包含有activity_id,所以很容易得到父文档IDA84致力于为用户收集丰富的生活经验知识

put子文档数据时候,必须指定父文档ID,就是父文档中的_id,这样父子数据才建立了关联关系。与此同时还要指定routing字段为父文档ID,这样保证了父子数据在同一分片上。A84致力于为用户收集丰富的生活经验知识

PUT /coupon/_doc/coupon_12345678?routing=activity_id_100A84致力于为用户收集丰富的生活经验知识

{A84致力于为用户收集丰富的生活经验知识

"coupon_id": 12345678,A84致力于为用户收集丰富的生活经验知识

"coupon_amount": "5",A84致力于为用户收集丰富的生活经验知识

"activity_id": 100,A84致力于为用户收集丰富的生活经验知识

"activity_coupon_field": {A84致力于为用户收集丰富的生活经验知识

"name": "coupon",A84致力于为用户收集丰富的生活经验知识

"parent": "activity_id_100" //父IDA84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

四、关联查询

1、has_parent查询(父查子)

根据父文档条件字段查询符合条件的子文档数据A84致力于为用户收集丰富的生活经验知识

例如:查询包含“年货节”活动字样,且已经被领取过的券A84致力于为用户收集丰富的生活经验知识

{A84致力于为用户收集丰富的生活经验知识

"query": {A84致力于为用户收集丰富的生活经验知识

"bool": {A84致力于为用户收集丰富的生活经验知识

"must": [{A84致力于为用户收集丰富的生活经验知识

"parent_type": "activity",A84致力于为用户收集丰富的生活经验知识

"has_parent": {A84致力于为用户收集丰富的生活经验知识

"query": {A84致力于为用户收集丰富的生活经验知识

"bool": {A84致力于为用户收集丰富的生活经验知识

"must": [{A84致力于为用户收集丰富的生活经验知识

"term": {A84致力于为用户收集丰富的生活经验知识

"status": {A84致力于为用户收集丰富的生活经验知识

"value": 1A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}, {A84致力于为用户收集丰富的生活经验知识

"wildcard": {A84致力于为用户收集丰富的生活经验知识

"activity_name": {A84致力于为用户收集丰富的生活经验知识

"wildcard": "*年货节*"A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}]A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}]A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

2、has_child查询(子查父)

根据子文档条件字段符合条件的父文档数据A84致力于为用户收集丰富的生活经验知识

例如:查询coupon_id=12345678在那个存在于哪个券活动中A84致力于为用户收集丰富的生活经验知识

{A84致力于为用户收集丰富的生活经验知识

"query": {A84致力于为用户收集丰富的生活经验知识

"bool": {A84致力于为用户收集丰富的生活经验知识

"must": [{A84致力于为用户收集丰富的生活经验知识

"has_child": {A84致力于为用户收集丰富的生活经验知识

"type": "coupon",A84致力于为用户收集丰富的生活经验知识

"query": {A84致力于为用户收集丰富的生活经验知识

"bool": {A84致力于为用户收集丰富的生活经验知识

"must": [{A84致力于为用户收集丰富的生活经验知识

"term": {A84致力于为用户收集丰富的生活经验知识

"coupon_id": {A84致力于为用户收集丰富的生活经验知识

"value": 12345678A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}]A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}]A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

}A84致力于为用户收集丰富的生活经验知识

参考:Joining queries | Elasticsearch Guide [7.9] | ElasticA84致力于为用户收集丰富的生活经验知识

以上文中如有不正之处欢迎留言指正A84致力于为用户收集丰富的生活经验知识

A84致力于为用户收集丰富的生活经验知识

作者:京东零售 李振乾
内容来源:京东云开发者社区

A84致力于为用户收集丰富的生活经验知识

也许你还喜欢

pr如何压缩视频大小操作步骤图文详

接下来要讲解的是pr压缩视频大小,以下是解决方案。

wmv用什么播放器合适?给您推荐几款

我们都知道,电脑上的视频文件有各种各样的格式,其中就包括WMV格式。那么,遇到WMV格式的视

更多网站资源丰富的搜索平台介绍

分享8个资源超级丰富的网站,涵盖了各种影视、音乐、壁纸、电子书等类型的资源内容,几乎

电动机接线图最新分享

以下分享10+年电工常用的41例接线方法,都是经过实践项目验证,并且可以直接拿来使用,一起

免费网络推广渠道以及方法详细介绍

今天,小编就为大家盘点全网十大优质渠道,帮助您轻松实现产品营销和项目推广。

epub电子书下载网站强力推荐

分享8个免费电子书网站,epub格式的书籍类型全面,能帮你找到90%的好书,而且下载方便,不需要

微信拍一拍好友图文教程详解

微信如何“拍一拍”好友呢?今天就教大家如何操作使用方法步骤。

鹅鸭杀由于网络问题无法进入房间

鹅鸭杀不显示游戏房间、无法加入房间是游戏网络不适合本地网络的原因,鹅鸭杀作为一款海

传统制造企业有必要建设增材制造中

在我们日常娱乐和日常工作中,我们如果想将FLV格式视频转换为MP4文件该怎么办呢?今天就

验证码无法显示怎么办图文介绍

验证码图片有些时候不能显示,那我们怎么办呢?下面将为大家讲解关于验证码无法显示的解决