Elasticsearch2.x Filter执行流程及缓存原理

一、基本概念

这里说的Filter非Post filter,关于Post filter这里不做介绍,我们主要关注的是在执行一个评分查询前执行的Filter。

在讲述ES Filter的执行流程和缓存原理之前,有几个概念我们必须要清楚。

Query and Filter context 

官网地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html

1. Query Context

所谓查询上下文,是指包含一个或多个查询条件的逻辑组合,QueryContext里包含的的单个查询表示的含义是“文档和该查询条件的匹配度有多大”,针对QueryContext里的每一个查询都会计算出一个_score来表示匹配度,整个QueryContext的匹配度评分等于其包含所有单个查询评分的总和,所以针对一个Query我们更多关注的是匹配度。

2.Filter Context

过滤器上下文和查询上下文类似,都是由1个或多个子查询(Query clause) 组成的。他们的区别在于Filter不会计算文档得分,它代表的含义是“文档和该查询条件是否匹配”,所以针对一个Filter我们更多关注的是能否可以过滤掉文档,返回的结果只能是可以或者不可以(true or false)。

二、基本查询

在写基本查询前我们先创建一个索引(my_store)和一个类型(products)并插入4条product数据

 

[java] view plain copy
 
  1. curl -XPUT http://localhost:9200/my_store  
  2. curl -XPOST http://localhost:9200/my_store/products/_mapping -d '  
  3. {  
  4.     "products": {  
  5.         "properties": {  
  6.             "productID": {  
  7.                 "type": "string",  
  8.                 "index": "not_analyzed"  
  9.             },  
  10.             "price": {  
  11.                 "type": "double"  
  12.             }  
  13.         }  
  14.     }  
  15. }'  
  16.   
  17. curl -XPOST http://localhost:9200/my_store/products/1 -d '{"productID":"XHDK-A-1293-#fJ3","price":10}'  
  18. curl -XPOST http://localhost:9200/my_store/products/2 -d '{"productID":"KDKE-B-9947-#kL5","price":20}'  
  19. curl -XPOST http://localhost:9200/my_store/products/3 -d '{"productID":"JODL-X-1937-#pV7","price":30}'  
  20. curl -XPOST http://localhost:9200/my_store/products/4 -d '{"productID":"QQPX-R-3956-#aD8","price":40}'  

插入数据后通过head插件看到的数据如下

 


要执行一个基于过滤器的查询必须要用bool query这样才能把过滤器嵌入到查询中,一个bool query的DLS基本格式是这样的

 

[java] view plain copy
 
  1. {  
  2.   "query": {  
  3.     "bool": {  
  4.       "must": {...},  
  5.       "must_not": {...},  
  6.       "should": {...},  
  7.       "filter": {...}  
  8.     }  
  9.   }  
  10. }  

当存在Filter这一项时,其他3项(must,must_not,should)是可选的,如果不传将默认match_all。

 

在执行上面这个查询时,Filter会优先于其他项执行,其他的Query会在过滤器执行的结果集中再执行,这样可以在真正执行评分查询前过滤掉大部分数据,这也正是过滤器的主要功能。下面的例子是查询productID等于“XHDK-A-1293-#fJ3”的DLS

 

[java] view plain copy
 
  1. {  
  2.   "query": {  
  3.     "bool": {  
  4.       "filter": {  
  5.         "term": {  
  6.           "productID": "XHDK-A-1293-#fJ3"  
  7.         }  
  8.       }  
  9.     }  
  10.   }  
  11. }  

这个查询我们只指定Filter这一项,所以返回的结果就是filter过滤后的结果且未执行文档评分。

 

三、Filter执行流程

下面的截图是官网对Filter执行流程的描述

官网地址:https://www.elastic.co/guide/en/elasticsearch/guide/current/_finding_exact_values.html#_finding_exact_values

 

这里先简单的翻译下,ES在执行非评分查询(这里的非评分查询我们以上面TermQuery查询为例)时会依次执行以下几个操作

1.查找匹配文档

TermQuery先在倒排索引中匹配到符合指定查询值的文档列表,这个case中是查询值是productID=XHDK-A-1293-#fJ3的文档也就是_id=1的文档。

2.创建一个bitset(位图)

Filter根据第1步的查询结果创建一个位图(一个只包含0和1的数组) ,用来表示文档是否被包含在这个TermQuery中,在我们的例子中这个bitset是[1,0,0,0]  它表示第1个文档匹配,第2、3.、4不匹配。在ES内部该位图采用“roaring bitmap”算法进行位图压缩,它能够高效的压缩稀疏和稠密集合。想深入了解的同学请自行google。

3.迭代生成的位图

在多个TermQuery同时存在一个FilterContext 中执行过滤查询时,会生成多个位图(一个Query clause对应一个BitSet),ES会迭代这些位图并从中找到符合条件的文档。当然ES会很智能的选择位图的执行顺序,通常情况下ES会选择稀疏的位图优先执行,这样的做的目的是过滤掉大部分不符合条件的文档。

4.叠加Bitset的执行次数

ES能缓存非评分查询并快速访问,但是它会很蠢的缓存一些不常使用的东东,在倒排索引中非评分查询已经相当快了,所以我们需要缓存那些“我们知道会在接下来的时间被多次访问的查询” 以免造成资源浪费。

为了这样做,ES跟踪记录了以每个索引为基础的历史访问记录,如果一个查询在最近的256个查询中被执行了若干次,那么它将会被缓存到内存中(一个非评分查询被缓存实际上是该查询的位图被缓存),当位图被缓存时还有两个条件是要满足的:该查询对应的segments所持有的文档数必须大于1W 且必须大于总索引大小的3%,ES这样做是因为对于那些小的索引段会很快的被合并掉,对它们的缓存其实是一种浪费

 

上面的流程可能看得不是很明白,下面我们举一个实际的例子来说明下,看下面这个查询。

 

[java] view plain copy
 
  1. {  
  2.   "from" : 0,  
  3.   "size" : 5,  
  4.   "query" : {  
  5.     "bool" : {  
  6.       "filter" : {  
  7.         "bool" : {  
  8.           "must" : [ {  
  9.             "term" : {  
  10.               "productID" : "JODL-X-1937-#pV7"  
  11.             }  
  12.           }, {  
  13.             "range" : {  
  14.               "price" : {  
  15.                 "from" : 20,  
  16.                 "to" : null,  
  17.                 "include_lower" : true,  
  18.                 "include_upper" : true  
  19.               }  
  20.             }  
  21.           } ]  
  22.         }  
  23.       }  
  24.     }  
  25.   }  
  26. }  

这个查询中的Filter Context中包含2个Query clause,一个是term查询,一个是range查询。

 

第一步,根据term和range查询得到两个BitSet,这里应该是term -> [0,0,1,0]和range ->  [0,1,1,1]

第二步,合并两个BitSet:[0,0,1,0] +  [0,1,1,1] = [0,0,1,0],合并到可以看出只有第3个文档符合条件,返回查询结果

第三步,分别缓存term和range查询生成的Bitset,以便其他查询包含这两个query clause的时候可以直接从缓存读取,以后每次命中缓存都会对对应的BiSet调用次数+1

四、Filter Cache(Query Cache)

这里的过滤器缓存其实指的就是我们所了解的查询缓存(非Query Request缓存),下面是官网对Query Cache的描述

官网地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-cache.html

 

上面描述的以及之前讨论过的的可以简单把Query Cache总结为以下几点

1.  Query Cache是节点级别的,每个节点上的所有分片共享一份缓存

2.  Query Cache采用LRU缓存失效策略

3.  Query Cache只能在Filter Context中被使用

4.  Query Cache可以通过 indices.queries.cache.size来设置缓存占用大小,默认10%,可手动设置比如512mb

5.  Query Cache实际缓存的是Bitset(位图),一个Query clause对应一个Bitset

6.  缓存要生效,必须满足两个条件

        a)查询对应的segments所持有的文档数必须大于10000

        b)查询对应的segments所持有的文档数必须大于整个索引size的3%

7.  当新索引文档时,Query Cache不会重新计算,而是判断索引的文档是否符合缓存对应的Query clause,满足则加入BitSet中

 

转自:http://blog.csdn.net/chennanymy/article/details/52403594

posted @ 2018-03-21 10:33  程序猿小硕  阅读(1494)  评论(0编辑  收藏  举报