Elasticsearch 多索引 join 实践
注:本文采用的实现语言是python,用到了python中的第三方库。
作者:aideny
前言
博主近期在开发一个解析引擎,把我们定义的json格式查询语言解析成多种数据源的查询语言,然后可以从服务器上的数据源中查询到结果。就类似数据库理论中视图层的工作吧。
这个解析引擎的工作流程就像下面这样:
通过这张图就可以理解博主需要做的工作,就是在后台支持一个实时的查询,用户是无法感知到后台不同数据源之间的查询差异的。
要真正实现一个广义的数据查询引擎,join是一个避不开的问题。后台有一个数据源是ES,由于这是一个实时的解析过程,而且只是一个解析的过程,所以不能更改数据源中已有的数据结构,只能想办法支持怎么去查询它。
博主在这里总结一下,是怎么支持ES的join查询。这里采用的一个方案是比较通用的,不依赖于ES特定版本提供的查询API。因为ES不同版本之间的DSL写法也存在一定的差异。
通用方案
经过调研,支持ES join 有这么一些方法:
- 完全不join,把关联表的字段融合到一张表里。当然这会造成数据的冗余
- 录入的时候join:使用 nested documents(nested document和主文档是同segment存储的,对于一个symbol,几千万个quote这样的场景就不适合了)
- 录入的时候join:使用 siren
- 查询时join:使用 parent/child (这个是elasticsearch的特性,要求parent/child同shard存在)
- 查询时join:使用 siren-joins(就是一个在服务端求值的filter,然后把结果发布给每个shard去做二次match)
- 查询时join:在客户端拼装第二个查询(和siren-joins差不多,但是多了一次客户端到服务器的来回)
- 在内存中join
由于我需要做的工作是针对已有的“表”进行一个实时的查询,而且线上的ES还有版本差异,有5.x的,有6.x的。所以就不能采用依赖表结构和ES的语法特性了,最后采用的方案是在内存中join。这也是一个通用的方案,不依赖es版本,不依赖于语法特性,只要单表能查,那么就能够join。
具体做法
观察单表查询结果
首先可以观察一下对单索引查询的一个结果,看看它的结构是什么样子。
单索引查询的DSL:
{
"query": {
"bool":{
"must":[
{
"range":{
"data_time":{
"lte":"2019-11-28T14:07:32.845+0800"
}
}
}
]
}
},
"sort":[
{
"device":{
"order":"asc"
}
}
],
"aggs":{
"groupby":{
"terms":{
"script": {
"source":"'p_id:' + doc['p_id'].value + ';' + 'entrance_time:' + doc['entrance_time'].value"
}
},
"aggs":{
"version__count":{
"value_count": {
"field": "version"
}
}
}
}
}
}
查询结果:
{
"took": 54,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped":