使用filters优化查询

ElasticSearch支持多种不同类型的查询方式,这一点大家应该都已熟知。但是在选择哪个文档应该匹配成功,哪个文档应该呈现给用户这一需求上,查询并不是唯一的选择。ElasticSearch 查询DSL允许用户使用的绝大多数查询都会有各自的标识,这些查询也以嵌套到如下的查询类型中:

  • constant_score
  • filterd
  • custom_filters_score

那么问题来了,为什么要这么麻烦来使用filtering?在什么场景下可以只使用queries? 接下来就试着解决上面的问题。

过滤器(Filters)和缓存

首先,正如读者所想,filters来做缓存是一个很不错的选择,ElasticSearch也提供了这种特殊的缓存,filter cache来存储filters得到的结果集。此外,缓存filters不需要太多的内存(它只保留一种信息,即哪些文档与filter相匹配),同时它可以由其它的查询复用,极大地提升了查询的性能。设想你正运行如下的查询命令:

{
"query" : {
"bool" : {
"must" : [
{
"term" : { "name" : "joe" }
},
{
"term" : { "year" : 1981 }
}
]
}
}
}

该命令会查询到满足如下条件的文档:name域值为joe同时year域值为1981。这是一个很简单的查询,但是如果用于查询足球运动员的相关信息,它可以查询到所有符合指定人名及指定出生年份的运动员。

如果用上面命令的格式构建查询,查询对象会将所有的条件绑定到一起存储到缓存中;因此如果我们查询人名相同但是出生年份不同的运动员,ElasticSearch无法重用上面查询命令中的任何信息。因此,我们来试着优化一下查询。由于一千个人可能会有一千个人名,所以人名不太适合缓存起来;但是年份比较适合(一般year域中不会有太多不同的值,对吧?)。因此我们引入一个不同的查询命令,将一个简单的query与一个filter结合起来。

{
"query" : {
"filtered" : {
"query" : {
"term" : { "name" : "joe" }
},
"filter" : {
"term" : { "year" : 1981 }
}
}
}
}

我们使用了一个filtered类型的查询对象,查询对象将query元素和filter元素都包含进去了。第一次运行该查询命令后,ElasticSearch就会把filter缓存起来,如果再有查询用到了一样的filter,就会直接用到缓存。就这样,ElasticSearch不必多次加载同样的信息。

并非所有的filters会被默认缓存起来

缓存很强大,但实际上ElasticSearch在默认情况下并不会缓存所有的filters。这是因为部分filters会用到域数据缓存(field data cache)。该缓存一般用于按域值排序和faceting操作的场景中。默认情况下,如下的filters不会被缓存:

  • numeric_range
  • script
  • geo_bbox
  • geo_distance
  • geo_distance_range
  • geo_polygon
  • geo_shape
  • and
  • or
  • not

尽管上面提到的最后三种filters不会用到域缓存,它们主要用于控制其它的filters,因此它不会被缓存,但是它们控制的filters在用到的时候都已经缓存好了。

更改ElasticSearch缓存的行为

ElasticSearch允许用户通过使用_chache和_cache_key属性自行开启或关闭filters的缓存功能。回到前面的例子,假定我们将关键词过滤器的结果缓存起来,并给缓存项的key取名为year_1981_cache,则查询命令如下:

{
"query" : {
"filtered" : {
"query" : {
"term" : { "name" : "joe" }
},
"filter" : {
"term" : {
"year" : 1981,
"_cache_key" : "year_1981_cache"
}
}
}
}
}

也可以使用如下的命令关闭该关键词过滤器的缓存:

{
"query" : {
"filtered" : {
"query" : {
"term" : { "name" : "joe" }
},
"filter" : {
"term" : {
"year" : 1981,
"_cache" : false
}
}
}
}
}

为什么要这么麻烦地给缓存项的key取名

上面的问题换个说法就是,我有是否有必要如此麻烦地使用_cache_key属性,ElasticSearch不能自己实现这个功能吗?当然它可以自己实现,而且在必要的时候控制缓存,但是有时我们需要更多的控制权。比如,有些查询复用的机会不多,我们希望定时清除这些查询的缓存。如果不指定_cache_key,那就只能清除整个过滤器缓存(filter cache);反之,只需要执行如下的命令即可清除特定的缓存:

curl -XPOST 'localhost:9200/users/_cache/clear?filter_keys=year_1981_cache'

什么时候应该改变ElasticSearch 过滤器缓存的行为

当然,有的时候用户应该更多去了解业务需求,而不是让ElasticSearch来预测数据分布。比如,假设你想使用geo_distance 过滤器将查询限制到有限的几个地理位置,该过滤器在请多查询请求中都使用着相同的参数值,即同一个脚本会在随着过滤器一起多次使用。在这个场景中,为过滤器开启缓存是值得的。任何时候都需要问自己这个问题“过滤器会多次重复使用吗?”添加数据到缓存是个消耗机器资源的操作,用户应避免不必要的资源浪费。

关键词查找过滤器

缓存和标准的查询并不是全部内容。随着ElasticSearch 0.90版本的发布,我们得到了一个精巧的过滤器,它可以用来将多个从ElasticSearch中得到值作为query的参数(类似于SQL的IN操作)。

让我们看一个简单的例子。假定我们有在一个在线书店,存储了用户,即书店的顾客购买的书籍信息。books索引很简单(存储在books.json文件中):

{
"mappings" : {
"book" : {
"properties" : {
"id" : { "type" : "string", "store" : "yes", "index" :
"not_analyzed" },
"title" : { "type" : "string", "store" : "yes", "index" :
"analyzed" }
}
}
}
}

上面的代码中,没有什么是非同寻常的;只有书籍的id和标题。 接下来,我们来看看clients.json文件,该文件中存储着clients索引的mappings信息:

{
"mappings" : {
"client" : {
"properties" : {
"id" : { "type" : "string", "store" : "yes", "index" :
"not_analyzed" },
"name" : { "type" : "string", "store" : "yes", "index" :
"analyzed" },
"books" : { "type" : "string", "store" : "yes", "index" :
"not_analyzed" }
}
}
}
}

索引定义了id信息,名字,用户购买书籍的id列表。此外,我们还需要一些样例数据:

curl -XPUT 'localhost:9200/clients/client/1' -d '{
"id":"1", "name":"Joe Doe", "books":["1","3"]
}'
curl -XPUT 'localhost:9200/clients/client/2' -d '{
"id":"2", "name":"Jane Doe", "books":["3"]
}'
curl -XPUT 'localhost:9200/books/book/1' -d '{
"id":"1", "title":"Test book one"
}'
curl -XPUT 'localhost:9200/books/book/2' -d '{
"id":"2", "title":"Test book two"
}'
curl -XPUT 'localhost:9200/books/book/3' -d '{
"id":"3", "title":"Test book three"
}'

接下来想象需求如下,我们希望展示某个用户购买的所有书籍,以id为1的user为例。当然,我们可以先执行一个请求 curl -XGET 'localhost:9200/clients/client/1'得到当前顾客的购买记录,然后把books域中的值取出来,执行第二个查询:

curl -XGET 'localhost:9200/books/_search' -d '{
"query" : {
"ids" : {
"type" : "book",
"values" : [ "1", "3" ]
}
}
}'

这样做太麻烦了,ElasticSearch 0.90版本新引入了 关键词查询过滤器(term lookup filter),该过滤器只需要一个查询就可以将上面两个查询才能完成的事情搞定。使用该过滤器的查询如下:

curl -XGET 'localhost:9200/books/_search' -d '{
"query" : {
"filtered" : {
"query" : {
"match_all" : {}
},
"filter" : {
"terms" : {
"id" : {
"index" : "clients",
"type" : "client",
"id" : "1",
"path" : "books"
},
"_cache_key" : "terms_lookup_client_1_books"
}
}
}
}
}'

请注意_cache_key参数的值,可以看到其值为terms_lookup_client_1_books,它里面包含了顾客id信息。请注意,如果给不同的查询设置了相同的_cache_key,那么结果就会出现不可预知的错误。这是因为ElasticSearch会基于指定的key来存储查询结果,然后在不同的查询中复用。 接下来看看上述查询的返回值:

{
...
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [ {
"_index" : "books",
"_type" : "book",
"_id" : "1",
"_score" : 1.0, "_source" : {"id":"1", "title":"Test book one"}
}, {
"_index" : "books",
"_type" : "book",
"_id" : "3",
"_score" : 1.0, "_source" : {"id":"3", "title":"Test book three"}
} ]
}
}

这正是我们希望看到的结果,太棒了!

term filter的工作原理

回顾我们发送到ElasticSearch的查询命令。可以看到,它只是一个简单的过滤查询,包含一个全量查询和一个terms 过滤器。只是该查询命令中,terms 过滤器使用了一种不同的技巧——不是明确指定某些term的值,而是从其它的索引中动态加载。

可以看到,我们的过滤器基于id域,这是因为只需要id域就整合其它所有的属性。接下来就需要关注id域中的新属性:index,type,id,和path。idex属性指明了加载terms的索引源(在本例中是clients索引)。type属性告诉ElasticSearch我们的目标文档类型(在本例中是client类型)。id属性指明的我们在指定索引的指文档类型中的目标文档。最后,path属性告诉ElasticSearch应该从哪个域中加载term,在本例中是clients索引的books域。 总结一下,ElasticSearch所做的工作就是从clients索引的client文档类型中,id为1的文档里加载books域中的term。这些取得的值将用于terms filter来过滤从books索引(命令执行的目的地是books索引)中查询到的文档,过滤条件是文档id域(本例中terms filter名称为id)的值在过滤器中存在。

elasticsearch filters特性的更多相关文章

  1. ElasticStack学习(七):ElasticSearch之Mapping初探

    一.Mapping的概念 1.Mapping类似于数据库中的Schema的定义,作用如下: 1)定义索引中的字段的名称: 2)定义字段的数据类型,例如字符串.数字.日期.布尔等: 3)对每个字段进行倒 ...

  2. 学习下ElasticSearch

    ElasticSearch基础概念 Elasticsearch的Head插件安装 Elasticsearch在Centos 7上的安装常见的问题 使用场景:比如分库的情况下,你想统计所有数据的报表,就 ...

  3. 《读书报告 -- Elasticsearch入门 》-- 安装以及简单使用(1)

    <读书报告 – Elasticsearch入门 > 第一章 Elasticsearch入门 Elasticsearch是一个实时的分布式搜索和分析引擎,使得人们可以在一定规模上和一定速度上 ...

  4. Elasticsearch 文档专用

    ES安装等操作 http://blog.csdn.net/cnweike/article/details/33736429 https://www.elastic.co/guide/cn/elasti ...

  5. ELK之Elasticsearch

    安装并运行Elasetisearch cd elasticsearch-<version> ./bin/elasticsearch 如果你想把 Elasticsearch 作为一个守护进程 ...

  6. 可以执行全文搜索的原因 Elasticsearch full-text search Kibana RESTful API with JSON over HTTP elasticsearch_action es 模糊查询

    https://www.elastic.co/guide/en/elasticsearch/guide/current/getting-started.html Elasticsearch is a ...

  7. 如何在python中使用Elasticsearch

    什么是 Elasticsearch ​ 想查数据就免不了搜索,搜索就离不开搜索引擎,百度.谷歌都是一个非常庞大复杂的搜索引擎,他们几乎索引了互联网上开放的所有网页和数据.然而对于我们自己的业务数据来说 ...

  8. ef core SoftDelete Multi-tenancy 软删除、多租户实现 Global Query Filters

    ef core提供了Global Query Filters特性来实现多租户与软删除,收集了一些实现方法. 最简单的例子时微软官方的特性解释. https://docs.microsoft.com/e ...

  9. PB级数据实时查询,滴滴Elasticsearch多集群架构实践

    PB级数据实时查询,滴滴Elasticsearch多集群架构实践  mp.weixin.qq.com 点击上方"IT牧场",选择"设为星标"技术干货每日送达 点 ...

随机推荐

  1. 【转】40个Java多线程问题总结

    文章转自 五月的仓颉 http://www.cnblogs.com/xrq730/p/5060921.html 前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习, ...

  2. PHP算法之Z 字形变换

    将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下: L C I RE T ...

  3. 一张图轻松掌握 Flink on YARN 应用启动全流程(上)

    Flink 支持 Standalone 独立部署和 YARN.Kubernetes.Mesos 等集群部署模式,其中 YARN 集群部署模式在国内的应用越来越广泛.Flink 社区将推出 Flink ...

  4. csps模拟93序列,二叉搜索树,走路题解

    题面: 模拟93考得并不理想,二维偏序没看出来,然而看出来了也不会打 序列: 对a,b数列求前缀和,那么题意转化为了满足$suma[i]>=suma[j]$且$sumb[i]>=sumb[ ...

  5. python相关软件安装流程图解——MySQL 8.0.13安装教程(windows 64位)——MYSQL依赖的软件——MYSQL必须的系统DLL插件——MYSQL真正的安装

    https://www.mysql.com/https://www.mysql.com/downloads/https://dev.mysql.com/downloads/windows/https: ...

  6. spss modeler出现使用错误提

    spss modeler出现使用错误提 1.对字段"compensation汇总导出"指定的类型不充分 问题: 为了分析需要,我加了一个"字段选项"--&quo ...

  7. shiro real的理解,密码匹配等

    1 .定义实体及关系 即用户-角色之间是多对多关系,角色-权限之间是多对多关系:且用户和权限之间通过角色建立关系:在系统中验证时通过权限验证,角色只是权限集合,即所谓的显示角色:其实权限应该对应到资源 ...

  8. PHP面向对象之继承的基本思想

    图例 概念和说明 代码展示 <?php header('content-type:text/html;charset=utf-8'); //学生考试系统 class Student{ publi ...

  9. ims注册与注销

    ims注册流程: 步骤1:当IMS和SIM访问已经就绪时,IMC将在满足条件后触发IMS注册到IMCB.步骤2.3:IMCB请求IMSA/IMSM获取IMS PDN和P-CSCF,然后触发VoLTE ...

  10. mysql用户和权限

    1.创建用户 格式:grant 权限 on 数据库.* to 用户名@登录主机 identified by "密码" mysql>grant all privileges o ...