ElasticSearch Node QueryCache 原理分析

背景:
最近要重启在集群更新些功能和插件,所以整理下集群不合理的配置一起改了,无意间发现一个Index 的 Cache命中居然为0,以为是限制了,查看配置都正常,看来得查查问题出在哪了,以下是查找原因过程和关于QueryCache的整理。
在这里插入图片描述

1、ES QueryCache

Elasticsearch 包含三个类型的读缓冲,分别为 Node Query Cache 、 Shard Request Cache 、 Fielddata Cache。
本篇文章只介绍Node Query Cache,其它两个Cache后面在介绍,Elasticsearch 集群中的每个节点包含一个 Node Query Cache,作用域是Node实例,由该节点的所有 shard 共享,Cache 采用 LRU 算法,Node Query Cache 只缓存 filter 部分耗时高的查询类型。

2、问题分析

为了查找问题建了一个临时索引只有 name/age/两个字段。
1)确认配置没有禁止使用缓存,说明缓存创建应该没有问题,那么我们详细分析下什么条件下会产生QueryCache缓存吧。
2)确认 什么Query是可被缓存 分析过程如下:
写一个dsl包含rang范围查询 执行查询10次 没有被cache

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "xl"
          }
        },
        {
          "range": {
            "age": {
              "gte": "5"
            }
          }
        }
      ],
      "filter": []
    }
  }
}

再写一个包含filter过滤的range范围查询,执行10次,结果被cache。

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "xl"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "age": {
              "gte": "5"
            }
          }
        }
      ]
    }
  }
}

跟原码分析:
1)查询DSL用带有filter条件的那个,参与查询Query: +name:xl 和 #age:[5 TO 2147483647],传入search方法进行检索。(这里有个小知识 查询条件与或非对应符号"+"/" “/”-",那么"#"就是过滤了)
在这里插入图片描述
2)继续跟进createWeight方法,进入751行创建weight,大家行在跟源码行注意这个needsScores他是判断这个Query是否为CacheQuery的一个关键。
在这里插入图片描述
进入287行创建BooleanWight,这个Wight是后面用来计算得分使用,IDF就来自于这个对象中不过与这次分析无关。
在这里插入图片描述
进入BooleanWeight构造方法,循环Query创建Weight,下面这段是创建条件 +name:xl 的Weight,注意needsScores=true和c.isScoring()=true 方法。
在这里插入图片描述
进入c.isScoring()方法看到只有与和或返回true,其它为false。(也就是说filter和非可能会走缓存?)
在这里插入图片描述
继续54行跟进
在这里插入图片描述
148行继续跟进,这是创建weight的具体地方了,进入后我们看needsScores为true,752行判断为false,weight继续使用751行的weight(name:xl),也就是没有使用753行的queryCache.doCache生成QueryCache的Weight对像,那么我继续看filter会生成QueryCache吗?
在这里插入图片描述
age:[5 TO 214…] 的needsScores为false751行判断为true,所以生成了IndicesQueryCache对像,这个对象就是Cache的关键了,有了它就有被Cache的希望了。
在这里插入图片描述
最后参与查询的两个Query,TermQuery和IndicesQueryCache。
在这里插入图片描述
结论:只有filter才会走Cache,其它查询方式不进Cache。

3) 那些Query不能参与Cache

实验Query是否进入Cache时每个Query执行了10次,那么执行相同Query多少个次才可以被Cache?,继续跟入search方法 看看IndicesQueryCache里面怎么规定频率的。
在这里插入图片描述
省略几步来到BooleanWeight类的w.scoreSupplier方法执行Query,然后score算分,默认BM25公式。
在这里插入图片描述
忽略TermQuery只看CacheQuery,进入LRUQueryCache的scorerSupplier方法,Cache后生成的DocSet是在LRUQueryCache这个对象中完成的,继续跟进714行。
在这里插入图片描述
在这里插入图片描述
在shouldNeverCache方法中判断不参与Cache的Query有 TermQuery/MatchAllDocsQuery/MatchNoDocsQuery/BooleanQuery/DisjunnctionMaxQuery。
在这里插入图片描述
结论:filter中子查询包含以下子Query不能参与Cache,,TermQuery/MatchAllDocsQuery/MatchNoDocsQuery/BooleanQuery/DisjunnctionMaxQuery。

4) Cache与查询Query的频率

继续跟进,724行 这里有个中断,如果(Doc的个数/8)*5>214…判断不适合缓存。
在这里插入图片描述

在这里插入图片描述
741行 从系统中获取缓存的Docidset,如果没有则生成,这要交代一下,被缓存过的Query会从系统中保存份,用于再次使用,因为数据量比较大时这个生成Docidset的耗时比较长,要比原查询没有Cache要长,所以大家使用Cache时尽量使用周期长一点的Query,如这种时间查询可以把周期扩大到小时为单位。如果大家发现你的Query走了Cache但还会有一些耗时特别长的 可能要注意这了。
{
“range”: {
“my_date”: {
“gte”: “now-1y/h”,
“lte”: “now-1y/h”
}
}
}
在这里插入图片描述
跟进747行,

在这里插入图片描述
跟进121行isCostlv方法,
在这里插入图片描述
isCostly方法判断MultiTermQuery/MultiTermQueryConstantScoreWrapper/TermInSetQuery/Point*Query的Query查询超过2次会被Cache,其它Query需要5次,如果其中出现BooleanQuery和DisjunnctionMaxQuery时会少去1次。
简单解析TermInSetQuery因为我对这个也不熟悉,TermsQuery 与多个 TermQuery 的区别
当 terms 的个数较少的时候,TermsQuery 等效为 ConstantScoreQuery 内部包含多个 TermQuery:

Query q1 = new TermInSetQuery(new Term(“field”, “foo”), new Term(“field”, “bar”));
// 等效为下面的语句
BooleanQuery bq = new BooleanQuery();
bq.add(new TermQuery(new Term(“field”, “foo”)), Occur.SHOULD);
bq.add(new TermQuery(new Term(“field”, “bar”)), Occur.SHOULD);
Query q2 = new ConstantScoreQuery(bq);
当 terms 较多的时候,它将使用匹配的文档组合成一个位集,并在该位集上进行评分;此时查询效率比普通的 Bool 合并要更加高效。

当 terms 的个数较多时,TermsQuery 比多个 TermQuery 组合的查询效率更高。
在这里插入图片描述
在这里插入图片描述
结论:
MultiTermQuery/MultiTermQueryConstantScoreWrapper/TermInSetQuery/Point*Query的Query查询超过2次会被Cache,其它Query需要5次,如果其中出现BooleanQuery和DisjunnctionMaxQuery时会少去1次。

5) Segmemt与Cache的关系

看到这依然没有发现问题根源,继续查看LRUQueryCache类看看有没有什么限制,看构造方法,一个有10000和0.3f的限制,一个则没有,刚刚没有注意到那个IndicesQueryCache对象是怎么创建的回头找找。
在这里插入图片描述
IndicesQueryCache是从这返回的那么IndicesQueryCache从那创建的那?继续找IndicesQueryCache
在这里插入图片描述
发现IndicesQueryCache是传进来的
向上找,原来是由GateWay中的IndicesService.verifyInndexMetadatad创建的IndicesQueryCache,向构造函数传入了一个ES配置对象settings。在这里插入图片描述在这里插入图片描述
看一下IndicesQueryCache的构造函数,第64行 使用settings中的indices.queries.cache.all_segments参数来控制是否所有的段都参与cache,82行判断是否所有段参与cache,默认是false,截图中的true是我改的,看下85行走默认不是所有段都参与cache。
在这里插入图片描述
原来这边还有一个限制,默认每个段大于10000个doc或每个段的doc数大于总doc数的30%时才允许参与cache。
在这里插入图片描述

在这里插入图片描述
结论:Segment与Cache之间的关系,默认每个段大于10000个doc或每个段的doc数大于总doc数的30%时才允许参与cache,也可以通过设置让所有段参与Cache。

到此找出问题原因是我的那个索引近期因为数据量减少了,每个段不够10000或30%的条件,所以没有参加Cache。
解决办法可以通过Merge索引成1个段,如果还够10000可以通过Shrink缩小Shard,其它可以换个角度想想,每个段中doc数据都不够10000正常情况下应该也用不到Cache了。

6)Cache相关配置

1)indices.queries.cache.size:为每个节点配置缓存的内存大小,默认是10%,支持两种格式,一种是百分数,占节点heap的百分比,另一种是精确的值,如512mb,这个参数是静态的配置后需要重启节点生效。
2)indices.queries.cache.count: 配置缓存的总数量。
3)indices.queries.cache.all_segments: 用于是否在所有 Segment上启用缓存,默认是false,对文档数小于10000或者小于整个索引Doc的30%的Segment进行缓存。
4)index.queries.cache.enabled:属于index级别的配置,用来控制是否启用缓存,默认是开启的。

7)ES Cache 查看API

http://172.18.130.48:9200/_stats/query_cache?pretty&human
cache_size是cache query的条数,Cache_count=query条数*段数
在这里插入图片描述

8)过期策略

每次新添加一个cache都会检测下是否需要过期一些query。Query Cache作用域是段如果Segment被合并或者删除,那么也会清理掉对应的缓存,所以尽量把每个段控制在10000个Doc以上在merge。

总结:

1)只有Filter下的子Query才能参与Cache。
2)不能参与Cache的Query有TermQuery/MatchAllDocsQuery/MatchNoDocsQuery/BooleanQuery/DisjunnctionMaxQuery。
3)MultiTermQuery/MultiTermQueryConstantScoreWrapper/TermInSetQuery/Point*Query的Query查询超过2次会被Cache,其它Query要5次。
4)默认每个段大于10000个doc或每个段的doc数大于总doc数的30%时才允许参与cache。
5)结果集比较大的Query在Cache时尽量增加使用周期以免频繁Cache构建DocIdset。
6)Segment被合并或者删除,那么也会清理掉对应的缓存。

如果喜欢搜索技术请关注我的公众号吧
每天会持续更新搜索相关技术
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值