Elasticsearch 入门教程 – filter执行原理深度剖析(bitset机制与caching机制)

Query context

在查询上下文中使用的查询将计算相关性分数,不会被缓存。 只要过滤器上下文不适用,就使用查询上下文。

Filter context

在过滤器上下文中使用的查询将不会计算相关性分数,并且可以缓存。 过滤器上下文由以下引入:

  • constant_score查询

  • bool查询中的must_not和(新添加)filter参数

  • function_score查询中的filterfilters参数

  • 任何叫filter的API,例如post_filter搜索参数,或者在聚合和索引别名中(any API called filter, such as the post_filter search parameter, or in aggregations or index aliases)

 or 和 and通过bool实现

以前orand过滤器与bool过滤器有不同的执行模式。 (It used to be important to use and/or with certain filter clauses, and bool with others.)。

现在这个区别已被删除:现在bool查询足够智能,可以很好地处理这两种情况。 由于这种变化,现在orand过滤器是bool查询内部执行语法。 这些过滤器将来会被删除。

1.3 filtered查询 与 query过滤器 废弃

query过滤器已经废弃不再需要 – 所有查询都可以在查询或过滤器上下文中使用。

filtered查询已被弃用。 filtered查询如下:

GET _search
{
  "query": {
    "filtered": {
      "query": {
        "match": {
          "text": "quick brown fox"
        }
      },
      "filter": {
        "term": {
          "status": "published"
        }
      }
    }
  }
}

将查询和过滤器转换为bool查询中的mustfilter参数:

GET _search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "text": "quick brown fox"
        }
      },
      "filter": {
        "term": {
          "status": "published"
        }
      }
    }
  }
}

Filter自动缓存

     以前可以通过_cache选项来控制哪些过滤器被缓存,并提供一个自定义的_cache_key。 这些选项已被弃用,如果存在,将被忽略。

     过滤器上下文中使用的查询子句现在可以自动缓存。该算法考虑到使用频率,查询执行成本以及构建过滤器的成本。

    terms过滤器查找机制不在缓存文档内容.现在依赖于文件系统缓存.如果查找索引不是太大,建议通过设置index.auto_expand_replicas:0-all将其复制到所有节点,以消除网络开销。

1.5 Java API Query和Filter重构

      org.elasticsearch.index.queries.FilterBuilders从ElasticSearch2.0开始已被删除,作为查询和过滤器组合的一部分。 这些过滤器现在可以在QueryBuilders中使用具有相同名称的方法。所有可以接受FilterBuilder的方法现在也可以接受QueryBuilder。

以前使用方式:

FilterBuilders.boolFilter()  
    .must(FilterBuilders.termFilter("name", "张三"))  
    .mustNot(FilterBuilders.rangeFilter("age").from(28).to(30))  
    .should(FilterBuilders.termFilter("city", "北京"));

现在可以使用如下方式:

BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();boolQueryBuilder.must(QueryBuilders.termQuery("name", "张三"));boolQueryBuilder.must(QueryBuilders.rangeQuery("age").from(28).to(30));boolQueryBuilder.must(QueryBuilders.termQuery("city", "北京");ConstantScoreQueryBuilder queryBuilder = QueryBuilders.constantScoreQuery(boolQueryBuilder);

2. 深入理解Filter

在执行非评分查询时,Elasticsearch内部会执行多个操作.以下面查询为例:

curl -XPUT  'localhost:9200/my_store/products/1' -d '{"price" : 10, 
"productID" : "XHDK-A-1293-#fJ3"}';

curl -XPUT  'localhost:9200/my_store/products/2' -d '{"price" : 20, 
"productID" : "KDKE-B-9947-#kL5"}';

curl -XPUT  'localhost:9200/my_store/products/3' -d '{"price" : 30, 
"productID" : "JODL-X-1937-#pV7"}';

curl -XPUT  'localhost:9200/my_store/products/4' -d '{"price" : 40, 
"productID" : "QQPX-R-3956-#aD8"}';

例如我们想要查询产品ID为XHDK-A-1293-#fJ3的产品:

curl -XGET 'localhost:9200/my_store/products/_search?pretty' -d'
{    "query" : {        
     "constant_score" : { 
            "filter" : { 
                "term" : { 
                    "productID" : "XHDK-A-1293-#fJ3"
                }
            }
        }
    }
}'

2.1 查找匹配文档

     term查询在倒排索引中查找词条XHDK-A-1293-#fJ3,并检索包含该词条的所有文档。 在本例中,只有文件1具有我们正在寻找的词条。然后获取包含该 term 的所有文档。

   举例:

    date来举例

    word doc1 doc2 doc3

    2017-01-01 * *

    2017-02-02 * *

    2017-03-03 * * *

    filter:2017-02-02

    到倒排索引中一找,发现2017-02-02对应的document list是doc2,doc3

    

2.2 构建bitset

      然后,过滤器构建一个bitset – 一个包含1和0的数组,描述了哪些文档包含查找词条。 匹配文档的标志位是 1 。 在我们的例子中,bitset将是[1,0,0,0](只有文档1具有我们要查找的词条)。在内部,它表示成一个 "roaring bitmap",可以同时对稀疏或密集的集合进行高效编码。

  举例:所有上述的bitset 为:【0,1,1】 doc1:不匹配这个Filter,doc2和doc3 是匹配这个Filter

2.3 迭代bitset(s)

      一旦为每个查询生成了bitsets,Elasticsearch会遍历该bitsets,以找到满足所有过滤条件的匹配文档集合。 执行顺序是启发式的(The order of execution is decided heuristically),但通常最稀疏的bitsets是首先被迭代的(因为它排除了最大数量的文档)。

 举例:

    遍历每个过滤条件对应的bitset,优先从最稀疏的开始搜索,查找满足所有条件的document

    后面会讲解,一次性其实可以在一个search请求中,发出多个filter条件,每个filter条件都会对应一个bitset

    遍历每个filter条件对应的bitset,先从最稀疏的开始遍历 

    [0, 0, 0, 1, 0, 0]:比较稀疏

    [0, 1, 0, 1, 0, 1]  

    先遍历比较稀疏的bitset,就可以先过滤掉尽可能多的数据

    遍历所有的bitset,找到匹配所有filter条件的doc

    请求:filter,postDate=2017-01-01,userID=1

    postDate: [0, 0, 1, 1, 0, 0]

    userID:   [0, 1, 0, 1, 0, 1]

    遍历完两个bitset之后,找到的匹配所有条件的doc,就是doc4

    就可以将document作为结果返回给client了

2.4 增加使用计数器

    Elasticsearch 可以缓存非评分查询从而达到更快的访问,但是有一点不合理的地方是它也会缓存一些使用极少的东西。由于倒排索引的原因,非评分计算已经相当快了,所以我们只想缓存那些我们知道在后面会被再次使用的查询,以避免资源的浪费。

     为了实现上面的目标,Elasticsearch 会跟踪每个索引查询使用的历史。如果查询在最近的 256 次查询中会被多次用到,那么就会被缓存到内存中。而当bitset被缓存时,对于具有少于10,000个文档(或小于总索引大小的3%)的段,会省略缓存。这些小的段即将会消失,所以为它们缓存是一种浪费。

    比如postDate=2017-01-01,[0, 0, 1, 1, 0, 0],可以缓存在内存中,这样下次如果再有这个条件过来的时候,就不用重新扫描倒排索引,反复生成bitset,可以大幅度提升性能。在最近的256个filter中,有某个filter超过了一定的次数,次数不固定,就会自动缓存这个filter对应的bitset

(5)filter大部分情况下来说,在query之前执行,先尽量过滤掉尽可能多的数据

       query:是会计算doc对搜索条件的relevance score,还会根据这个score去排序

       filter:只是简单过滤出想要的数据,不计算relevance score,也不排序

(6)如果document有新增或修改,那么cached bitset会被自动更新

    postDate=2017-01-01,[0, 0, 1, 0]

      document,id=5,postDate=2017-01-01,会自动更新到postDate=2017-01-01这个filter的bitset中,全自动,缓存会自动更新。      postDate=2017-01-01的bitset,[0, 0, 1, 0, 1]

      document,id=1,postDate=2016-12-30,修改为postDate-2017-01-01,此时也会自动更新bitset,[1, 0, 1, 0, 1]

(7)以后只要是有相同的filter条件的,会直接来使用这个过滤条件对应的cached bitset

发表评论