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

一、join总述

1、关系类比

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

那么不禁有疑问问了,由于业务场景的决定,之前必须关联查询的两张表还能做到进行关联吗?

答案是可以的,es也提供了类似于关系型数据库的关联查询,但是它又与关系型数据的关联查询有明显的区别与限制。

2、使用场景

如果把关系数据库原有关联的两张表,同步到es后,通常情况下,我们业务开发中会有两种查询诉求的场景

场景1

诉求:展示子表维度的明细数据(包含父表和子表中字段的条件)

方案:对于此种查询诉求,我们可以把原来关联的父子表打成父子表字段混合在一起的大宽表,既能满足查询条件,又有查询性能的保障,也是常用存储方案之一

场景2

诉求:展示父表维度的明细数据(包含父表和子表中字段的条件)

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

这个时候我们就可以使用es提供的join功能来完成场景2的诉求查询,同时它也满足场景1的诉求查询

3、使用限制

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

(1)父子文档(数据)必须存储在同一index中

(2)父子文档(数据)必须存储在同一个分片中,通过关联父文档ID关联

(3)一个index中只能包含一个join字段,但是可以有多个关系

(4)同一个index中,一个父关系可以对应多个子关系,一个子关系只对应一个父关系

4、性能问题

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

不过,在笔者使用的过程中,在5个分片的前提下,且父表十万量级,子表数据量在千万量级的情况下,关联查询的耗时还是在100ms内完成的,对于B端许多场景还是可以接受的。

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

二、Mapping

1、举例说明

这里以优惠券活动与优惠券明细为例,在一个优惠券活动中可以发放几千万的优惠券,所以券活动与券明细是一对多的关系。

券活动表字段

字段 说明
activity_id 活动ID
activity_name 活动名称

券明细表字段

字段 说明
coupon_id 券ID
coupon_amount 券面额
activity_id 外键-活动ID

2、mapping释义

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

activity_coupon_field是一个关联字段,内部定义了一组join关系,该字段为自命名

type指定关联关系是join,固定写法

relations定义父子关系,activity父类型名称,coupon子类型名称,名称均为自命名

{
"mappings": {
"properties": {
"activity_coupon_field": {
"type": "join",
"relations": {
"activity": "coupon"
}
},
"activity_id": {
"type": "keyword"
},
"activity_name": {
"type": "keyword"
},
"coupon_id": {
"type": "long"
},
"coupon_amount": {
"type": "long"
}
}
}
}

三、插入数据

1、插入父文档

在put父文档数据的时候,我们通常按照某种规则指定文档ID,方便子文档数据变更时易于得到父文档ID。比如这里我们用activity_id的值:activity_100来作为父id

PUT /coupon/_doc/activity_100

{
"activity_id": 100,
"activity_name": "年货节5元促销优惠券",
"activity_coupon_field": {
"name": "activity"
}
}

2、插入子文档

上边已经指定了父文档ID,而子表中已经包含有activity_id,所以很容易得到父文档ID

put子文档数据时候,必须指定父文档ID,就是父文档中的_id,这样父子数据才建立了关联关系。与此同时还要指定routing字段为父文档ID,这样保证了父子数据在同一分片上。

PUT /coupon/_doc/coupon_12345678?routing=activity_id_100

{
"coupon_id": 12345678,
"coupon_amount": "5",
"activity_id": 100,
"activity_coupon_field": {
"name": "coupon",
"parent": "activity_id_100" //父ID
}
}

四、关联查询

1、has_parent查询(父查子)

根据父文档条件字段查询符合条件的子文档数据

例如:查询包含“年货节”活动字样,且已经被领取过的券

{
"query": {
"bool": {
"must": [{
"parent_type": "activity",
"has_parent": {
"query": {
"bool": {
"must": [{
"term": {
"status": {
"value": 1
}
}
}, {
"wildcard": {
"activity_name": {
"wildcard": "*年货节*"
}
}
}]
}
}
}
}]
}
}
}

2、has_child查询(子查父)

根据子文档条件字段符合条件的父文档数据

例如:查询coupon_id=12345678在那个存在于哪个券活动中

{
"query": {
"bool": {
"must": [{
"has_child": {
"type": "coupon",
"query": {
"bool": {
"must": [{
"term": {
"coupon_id": {
"value": 12345678
}
}
}]
}
}
}
}]
}
}
}

参考:Joining queries | Elasticsearch Guide [7.9] | Elastic

以上文中如有不正之处欢迎留言指正

作者:京东零售 李振乾

内容来源:京东云开发者社区

Elasticsearch 之 join 关联查询及使用场景的更多相关文章

  1. 子查询优化成join关联查询时要注意一对多关系

    mysql> select * from t where t.id in (select t1.tid from t1); +------+ | id | +------+ | +------+ ...

  2. left join 和inner join关联查询区别

    inner join 必须两边对应才能查处结果 left join 用主表关联副表,关联不出来依然显示结果

  3. MapReduce实战(五)实现关联查询

    需求: 利用MapReduce程序,实现SQL语句中的join关联查询. 订单数据表order: id date pid amount 1001 20150710 P0001 2 1002 20150 ...

  4. elasticsearch 关联查询

    父-子关系文档 父-子关系文档 在实质上类似于 nested model :允许将一个对象实体和另外一个对象实体关联起来. 而这两种类型的主要区别是:在 nested objects 文档中,所有对象 ...

  5. C#代码中实现两个表(DataTable)的关联查询(JOIN)

    之前通常都是使用SQL直接从数据库中取出表1和表2关联查询后的数据,只需要用一个JOIN就可以了,非常方便.近日遇到一种情况,两个表中的数据已经取到代码中,需要在代码中将这两个表关联起来,并得到它们横 ...

  6. Mysql多表表关联查询 inner Join left join right join

    Mysql多表表关联查询 inner Join left join right join

  7. 关联查询 join on 和比较运算符 in

    join on多表之间的关联查询 写法select 字段 from 表1 t join 表2 s on t.字段1 = s.字段1 where 条件: 也可以这么写select 字段 from 表1 ...

  8. SQL中order by;group up;like;关联查询join on的用法

    排序order by的用法: 1.order by 字段名1 asc/desc, 字段名2 asc/desc,... 先按照字段名1的升序/降续给表进行排列 然后 按照字段名2的升序/降续给表进行排列 ...

  9. oracle使用LEFT JOIN关联产生的问题在查询结果中使用CASE WHEN 无法判断

    oracle使用LEFT JOIN关联产生的问题在查询结果中使用CASE WHEN 无法判断 查询方式一: SELECT CASE WHEN (SELECT CAST(SUM(CASE ) THEN ...

  10. (一)SQL关联查询的使用技巧 (各种 join)

    ---恢复内容开始--- (一)SQL关联查询的使用技巧 (各种 join) 这几天因为工作的时候,发现自己的sql语句基础不是很好,特意研究了一下,发现sql语句真的是博大精深,sql语句不仅是要查 ...

随机推荐

  1. GCC编译器编译过程

    GCC编译器编译过程 #生成test可执行文件 g++ test.cpp -o test 其实,上述命令可以分解为以下几个步骤 1. 预处理-Pre-Processing # -E 选择指示编译器仅对 ...

  2. 第一章C语言概述

    1.1程序实例 //first.程序 #include <stdio.h> int main() { int num; num = 1; printf("I am a simpl ...

  3. mybatis-spring注解MapperScan的原理

    很多开发者应该都知道,我们只使用@MapperScan这个注解就可以把我们写的Mybatis的Mapper接口加载到Spring的容器中,不需要对每个Mapper接口加@Mapper这个注解了,加快了 ...

  4. java注解与反射--3

    java注解与反射--3 类的加载与ClassLoader java内存 堆 存放new的对象和数组 可以被所有的线程共享,不会存放别的对象引用 栈 存放基本变量类型 引用对象的变量 方法区 可以被所 ...

  5. HTML+JS+CSS实现图片文件上传界面设计的例子

    在Java Web应用程序设计中文件上传功能的实现是一项非常重要的工作,本文中的例子程序实现了文件上传功能的界面设计.在Java Web应用程序设计中涉及到上传图片文件功能时,往往都需要对图片文件进行 ...

  6. 如何用Python对股票数据进行LSTM神经网络和XGboost机器学习预测分析(附源码和详细步骤),学会的小伙伴们说不定就成为炒股专家一夜暴富了

    前言 最近调研了一下我做的项目受欢迎程度,大数据分析方向竟然排第一,尤其是这两年受疫情影响,大家都非常担心自家公司裁员或倒闭,都想着有没有其他副业搞搞或者炒炒股.投资点理财产品,未雨绸缪,所以不少小伙 ...

  7. 基于express、node与mongodb写后端接口

    1.首先运行mongodb,建立一个数据库.(前提是你的电脑已经装了mongodb) 先打开一个命令窗口打开mongodb cd /usr/localcd mongodb/bin./mongod -- ...

  8. C++/Qt网络通讯模块设计与实现(六)

    前面章节主要讲述网络通讯客户端的实现,各位小伙伴需认真阅读以及理解,理会其中的思想,有疑问的地方可及时给我私信,我都会非常认真地解答大家的疑惑. C++/Qt网络通讯模块设计与实现(一) C++/Qt ...

  9. 《HelloTester》第4期

    1.前言 终于到了谈面试的部分了! 我在这也说明一下,有同学说之前简历篇的时候一直在说项目的介绍,而面试官真正关心的是技术啊?我在这做个解释,因为我写的这些文章主要针对的是软件测试的同学,所以其他职位 ...

  10. [WEB]对于"Refused to execute script from 'http://xx.xx.xx/yy/zz.js' because its MIME type ('') is not executable, and strict MIME type checking is enabled."问题的解决办法

    1 文由 阶段一:对WEB服务器HTTP通信的header设置了安全头(X-Content-Options:nosniff) 两个月前协助交付侧大哥处理.修复一个三级等保项目的第三方安全公司释出的安全 ...