【ES】查询缓存

ES 内部有多种缓存用来提高查询效率,缓存类型如下:

  1. Shard Request Cache
  2. Node Query Cache (Filter Cache)
  3. Field Data Cache

Shard Request Cache

Shard Request Cache 简称 Request Cache,是分片级别的查询缓存。Request Cache 缓存的 key 是整个客户端请求,value 是单个分片的查询结果。

并非所有分片级别查询请求都会被缓存,Request Cache 的主要作用是对聚合的缓存,聚合过程是实时计算,通常会消耗很多资源,缓存对聚合来说意义重大。默认情况下,Request Cache 只对 size = 0的请求结果生效。

不被缓存的条件包括:

scroll、设置了 profile 属性,查询类型不是 QUERY_THEN_FETCH,以及设置了 requestCache = false 等请求类型。另外还包括一些存在不确定性的查询,例如:范围查询带有 now,由于它是毫秒级别的,缓存下来没有意义,类似的还有在脚本查询中使用了 Math.random()、new Date() 等函数的查询也不会进行缓存。

查询结果中被缓存的内容主要包括:

hits.total、aggregations(聚合结果)、以及 suggestions等。

Request Cache 缓存失效条件包括:

  1. 当 segment 变更,分片被刷新时,缓存会失效。
  2. 当缓存已满,会使用最近最少使用 LRU 原则删除缓存。默认情况下 ES 会给每个节点分配最多堆内存的 1% 作为 Request Cache。

Node Query Cache (Filter Cache)

Node Query Cache 也称为 Filter Cache,用来缓存 filter 子查询语句在 segment 上的结果。缓存的 key 为 filter 子查询,value 为查询结果,其中查询结果是匹配到的 document numbers,保存在位图 BitSet 中。当整个查询有多个 filter 子查询时,交并集直接对位图做位运算即可。

并非所有的 filter 查询都会被缓存,策略如下:

  • 某些类型的查询永远不会被缓存,例如:TermQuery、MatchAllDocsQuery、MatchNoDocsQuery、以及子查询为空的BooleanQuery、DisjunctionMaxQuery。
  • 某条 query 的访问频率大于等于特定阈值之后,该 query 结果才会被缓存。对于访问频率,主要分为 2 类,一类是访问 2 次就会被缓存,包括: MultiTermQuery、MultiTermQueryConstantScore、TermInSetQuery、PointQuery,其余类型的查询访问 5 次会被缓存。

由于 Filter Cache 是为每个 segment 建立的,当 segment 合并的时候,被删除的 segment 其关联缓存会失效。其次,对于体积较小的 segment 不会建立缓存,因为他们很快会被合并。只要当 segment 的 doc 数量大于 10000,并且占整个分片的 3% 以上才会走缓存策略。

默认情况下,缓存最多可容纳 10000 个查询,最大占总堆空间的 10%。当缓存已满,同样会使用最近最少使用 LRU 原则删除缓存。

Field Data Cache

fielddata 与 docValues 类似,都是用于支持排序、聚合以及脚本等需求,使用列式存储。不过 docValues 不支持 text 类型,而 fielddata 专用于 text 类型,默认被禁用。

Field Data Cache 包含字段数据和全局序号,用于支持 text 类型上的聚合。在首次将 text 类型字段用于聚合、排序或脚本场景时,ES 会按需构建 fielddata 数据结构。通过从磁盘读取每个 segment 的反向索引,反转术语 -> 文档关系并将结果存储在 JVM 堆中。默认情况下,Field Data Cache 大小是无限的,内存会不断增长,直到达到断路器设定的限制。如果达到断路器限制,ES 将阻止进一步增加缓存大小的请求。在这种情况下,可以手动清除缓存。

如果设置了内存大小限制,将使用 LRU 清除最近最少更新的条目,此设置会自动避免断路器限制,但代价是会根据需要重建缓存。

手动清理缓存

对于 ES 和 Lucene 层面的三种缓存,可以使用 REST API 手动清除。

清除指定索引或全部索引的缓存:

1
2
POST /<index>/_cache/clear    #清理指定索引的 cache,支持多个
POST /_cache/clear #清理整个集群的 cache

只清理特定缓存:

1
2
3
POST /<index>/_cache/clear?query=true     #只清理 query cache
POST /<index>/_cache/clear?request=true #只清理 request cache
POST /<index>/_cache/clear?fielddata=true #只清理 fields cache

通过 fields 参数指定清理部分字段的缓存:

1
POST /my-index/_cache/clear?fields=foo,bar 

不建议在生产环境中进行手动清除缓存,会对查询性能产生较大的影响,手动清除缓存一般只用于测试和验证场景。