《深入理解ElasticSearch》——2.7 使用过滤器优化查询

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介:

本节书摘来自华章计算机《深入理解ElasticSearch》一书中的第2章,第2.7节,作者:[美] 拉斐尔·酷奇(Rafa Ku) 马雷克·罗戈任斯基(Marek Rogoziński)更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.7 使用过滤器优化查询

ElasticSearch允许用户创建他们熟知的各种不同的查询类型。当需要决定哪些文档与查询匹配并应该返回时,仅有查询本身是不够的。ElasticSearch查询DSL提供的大多数查询类型都有它们的相似物,并且能将相似物包装(wrapping)成以下这些查询类型使用:

  • constant_score
  • filtered
  • custom_filters_score

那么问题来了:“什么时候使用过滤器,什么时候仅需使用查询类型?”。现在,让我们揭晓它的答案。
2.7.1 过滤器与缓存
首先,过滤器是很好的缓存候选方案,正如你所预料的那样,ElasticSearch提供了一种特殊的缓存,即过滤器缓存(filter cache),用来存储过滤器的结果。而且,被缓存的过滤器并不需要消耗过多的内存(因为它们只存储了哪些文档能与过滤器相匹配的相关信息),而且可供后续所有与之相关的查询重复使用,从而极大地提高了查询性能。想象一下,你执行了下面这个简单的查询:


3c50b8743694a2912a0ef10558eeea6aaa3e928a

该查询返回了在name字段包含joe以及在year字段包含1981的所有文档。尽管这个查询很简单,但是它能查询出满足指定姓名和出生年代条件的足球运动员。
在当前这种情形下,只有同时满足这两个条件的查询才会被缓存起来。因此,如果我们想搜索名字相同而出生年代不同的足球运动员,那么之前的查询就不再适用了。所以现在让我们想想如何来优化这个查询:人名有太多种可能,因此它不是完美的缓存候选对象,而出生年代则是(year字段中并没有很多不同的值,难道不是吗?)。于是,我们使用另外一种查询方法,该查询组合了查询类型与过滤器:


470979d0b657eebe7e4a770f6cae1ba01776fa6c

我们已经使用了filtered查询来同时包含查询和过滤器元素。第一次执行该查询以后,过滤器就会被ElasticSearch缓存起来,如果后续的其他查询也要使用该过滤器,则它将会被重复利用,从而避免ElasticSearch重复加载相关数据。
并不是所有过滤都默认被缓存
缓存很有用,但事实上ElasticSearch并不是默认缓存所有过滤器。这是因为在Elastic-Search中某些过滤器使用了字段数据缓存,这是一种特殊的缓存,它可以在基于字段数据的排序时使用,也能在计算切面结果时使用。以下过滤器默认不缓存:
  • numeric_range
  • script
  • geo_bbox
  • geo_distance
  • geo_distance_range
  • geo_polygon
  • geo_shape
  • and
  • or
  • not

上面提到的过滤器中,最后三个本身并不使用字段缓存,但由于它们操作其他过滤器,因而它们不缓存。而且,它们操作的过滤器在必要的时候已经被缓存起来了。
改变ElasticSearch的缓存行为
如果有需要,ElasticSearch允许用户通过设置_cache和_cache_key属性来开启或关闭过滤器的缓存机制。回到先前的例子,进行相关配置从而缓存词项过滤器结果,缓存的key为year_1981_cache:


728c340c377bf36c593e4c9a6e37962b71ec1823

现在,关闭该查询的词项过滤器缓存:

<a href=https://yqfile.alicdn.com/9dbe35dbcc501319aed2d0cbc7aef0ca2b89e794.png" >

为什么要为cache的key命名
也许读者会有疑问,为什么非要手动设置_cache_key属性,难道ElasticSearch不会自动处理它吗?当然,必要时可以让ElasticSearch自动处理,但有时候需要更精细地控制缓存行为,就需要手动处理了。例如,已知某些查询很少执行,但又想周期性地清除之前查询的缓存。如果不设置_cache_key,那么将不得不强制清理全部的过滤器缓存;而如果设置了_cache_key,只需执行下面的命令:

fb633a1fd66b821baee106af55e6f1688e02f479

何时改变ElasticSearch过滤器缓存的行为
有些时候,用户比ElasticSearch更清楚自己想要什么。例如,你也许想在查询时使用geo_distance过滤器使之只返回距离较近的文档,同时确保该过滤器能与其他多个具有相同脚本过滤器(script filter)参数的查询一起使用,从而将多次使用同一个脚本。在这种场景中,就值得开启这些过滤器的缓存。每时每刻用户都应该问自己:“我会多次使用该过滤器还是只使用一次?”由于将数据存放在缓存中会消耗资源,因而在不需要时应及时清理数据。
2.7.2 词项查找过滤器
缓存和各种标准查询并不是ElasticSearch的全部家当。随着ElasticSearch 0.90的发布,又一个精巧的过滤器可供我们使用了,它用于给一个具体的查询传递从ElasticSearch取回的多个词项(与SQL的IN操作符类似)。
现在看一个简单的例子。假设有一个在线书店,并且存储了书店客户所购买书籍的相关信息。索引book看起来非常简单(索引映射信息存储在books.js文件中):


0ec2e65fffcae64c9150068b0b84deac7dbb46cc

前面的代码并没有什么稀奇之处,它仅仅列出了书籍的ID和标题而已。
现在,我们再看看clients.json文件,这里存储了用于描述索引clients结构的映射信息:

c8b5331f2ad49f8e25afe844afd24ba562c1c070

我们有了客户ID、姓名以及客户所购买书籍的ID列表。除此之外,还索引了一些范例数据:

91fc26c7be77c0f6f51369d16917a951c39f0e78

现在,考虑一下如何获取某个特定用户购买的所有书籍,例如,ID为1的用户。当然,我们可以执行下面这个get请求:curl -XGET 'localhost:9200/clients/client/1',来获取存有该客户信息的文档,然后利用文档中的books字段信息来执行下面这个查询:


<a href=https://yqfile.alicdn.com/0f941fb897a3b424fd601883bddd56fae1105c70.png" >

幸运的是,我们有更简捷的做法,ElasticSearch 0.90新提供了一个查找过滤器,它允许用户将前面的两个查询合并为一个过滤查询(filtered query),如下面代码所示:

94a7674b89bbdafc48879901b83a2e6804ae8fe6

请注意参数_cache_key的值。正如你所见,它被设置为terms_lookup_client_1_books,并包含有用户的ID。但值得警惕的是,当在多个不同的查询中重用该_cache_key的值时,你可能会得到错误的或者是预料之外的检索结果。这是因为ElasticSearch会在某个特定的key下缓存某个查询的结果,并在其他查询执行时重用这些结果。
现在,来查看一下前面那个查询的响应结果:


<a href=https://yqfile.alicdn.com/c13f9455411587d96e2396c3205940a2061439c1.png" >

这正是我们想要的。看起来有点不可思议吧?
它是如何工作的
查看一下我们发送至ElasticSearch的查询。它只是一个简单的带过滤器的查询,即在一个匹配所有文档的查询上外加了一个terms过滤器。但是在这里,词项过滤器的使用方式略有不同,我们并没有显式确定具体的词项,而是让ElasticSearch去索引中加载它们。
正如你所见,我们感兴趣的是查询的id对象,这是因为它包含多个属性,且过滤器的执行也依赖于它。具体包含的属性有:index、type、id和path。index属性指明了所要加载词项所在的索引(本范例中是clients索引)。type属性告诉ElasticSearch我们对哪些文档类型感兴趣(本范例中是client)。id属性指示了需要加载对象所在文档的ID。path字段用来告诉ElasticSearch从哪个字段加载词项,在我们的范例中,指的是clients索引的books字段。
总而言之,ElasticSearch所做的就是从clients索引中ID为1,类型为client的文档的books字段中加载词项,并将这些词项用于词项过滤器中,从而筛选那些books索引(因为我们在该索引中执行查询)中id字段包括这些词项的文档(请注意词项过滤器的名字:id)。
请记住,为了使词项查找功能生效,需要先存储_source字段。
性能考虑
前面的查询执行在ElasticSearch内部已经通过缓存机制优化了,即相关词项已经被加载到过滤器缓存中且由查询指定的key下了。除此之外,一旦将这些词项(即那些书籍的ID)加载到缓存,后续的查询就不会再重复加载,这意味着ElasticSearch能提高此类查询的性能。
如果在词项查找过程中使用到的数据量不大,建议索引(如范例中的索引clients)只创建一个分片,并且在所有出现了books索引的节点上为该分片保存一个副本。之所以这么建议是因为ElasticSearch优先在本地执行词项查询以避免不必要的网络开销和延迟,进而能提高查询的性能。
从内部对象中加载词项
如果客户信息的books字段类型由数值类型数组变为内部对象数组,那么查询也应该做相应的修改,此时应修改查询的id属性,使之包含对象嵌套的相关信息。例如,将"id":"books"修改为"id":"books.book"。
词项查询过滤器缓存设置
前面提到过,为了提供词项查找功能,ElasticSearch引进了一种新的缓存类型,它使用了一种快速的LRU(最近最少使用算法)缓存来处理词项缓存。
想了解更多关于LRU缓存及其工作机制的内容,请参考: http://en.wikipedia.org/ wiki/Page_replacement_algorithm#Least_recently_used中的文档。
为了配置这种缓存,用户可以在elasticsearch.yml文件中配置下面这些属性:
  • indices.cache.filter.terms.size:默认设置ElasticSearch词项查找缓存的最大内存使用量为10mb。对于大多数案例来说,这个默认值够用了,但如果要加载更多的数据至缓存中,那么可以调大该参数值。
  • indices.cache.filter.terms.expire_after_access:该属性配置了自上次查询以来的超时时长。默认不超时。
  • indices.cache.filter.terms.expire_after_write:该属性配置了数据加载至缓存以后多长时间将超时。默认不超时。
相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
5天前
|
缓存 监控 安全
Elasticsearch扩展和优化
【11月更文挑战第4天】
20 6
|
1月前
|
存储 自然语言处理 Java
Elasticsearch写入优化
【10月更文挑战第3天】Elasticsearch:从写入原理谈写入优化
75 2
|
1月前
|
存储 JSON 监控
大数据-167 ELK Elasticsearch 详细介绍 特点 分片 查询
大数据-167 ELK Elasticsearch 详细介绍 特点 分片 查询
52 4
|
1月前
|
自然语言处理 搜索推荐 Java
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(一)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图
49 0
|
1月前
|
存储 自然语言处理 搜索推荐
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
34 0
|
2月前
|
JSON 自然语言处理 算法
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
DSL查询文档、RestClient查询文档、全文检索查询、精准查询、复合查询、地理坐标查询、分页、排序、高亮、黑马旅游案例
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
|
3月前
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
119 1
|
3月前
|
存储 自然语言处理 Java
ElasticSearch 实现分词全文检索 - 经纬度定位商家距离查询
ElasticSearch 实现分词全文检索 - 经纬度定位商家距离查询
30 0
|
3月前
|
自然语言处理 Java
ElasticSearch 实现分词全文检索 - 高亮查询
ElasticSearch 实现分词全文检索 - 高亮查询
64 0
|
3月前
|
缓存 自然语言处理 Java
ElasticSearch 实现分词全文检索 - filter查询
ElasticSearch 实现分词全文检索 - filter查询
46 0