Elasticsearch 缓存深入详解

2021-01-18 11:46:32 浏览数 (1)

1、Elasticsearch 缓存引出

Elasticsearch 查询的响应需要占用 CPU、内存资源,在复杂业务场景,会出现慢查询,需要花费大量的时间。

如何破局呢?增加集群硬件配置会有高昂硬件开销。还有没有其他方案呢?这时候会想到:缓存。

Elasticsearch 有哪些缓存,不同缓存的应用场景是什么呢?本文给出答案。

2、Elasticsearch 缓存经常被问道的问题汇总

  • 怎样知道自己的查询时命中缓存了还是走的磁盘搜索?
  • 我想每次查询的时候直接显示几个小时以前的数据 而不是只有实时的,这些是要存到缓存中么?
  • 看 Elasticsearch API的时候看到 /_cache/clear,这个缓存什么时候需要清理?有什么场景需要使用?

以上问题都是实战业务场景的问题。

3、Elasticsearch 缓存分类

3.1 节点查询缓存(Node query cache)

filter 过滤查询的结果缓存在节点查询缓存中,以便快速查找。

每个节点都有一个所有分片共享的查询缓存。缓存使用 LRU ( Least Recently Used 缓存淘汰策略)策略,当缓存已满时,优先清理最近最少使用的查询结果,以腾出空间存放新结果数据。

用户无法查看节点查询缓存的内容。

3.1.1 节点查询缓存适用场景
  • Term 查询和 filter 查询。
  • 除 Term 查询和 filter 查询之外使用的查询不符合缓存条件。
3.1.2 节点查询缓存内存上限

默认情况下,节点查询缓存最多可容纳10000个查询,最多占总堆空间的10%。

为了确定查询是否符合缓存条件,Elasticsearch 维护查询历史记录以跟踪事件的发生。

如果一个段至少包含 10000 个文档,并且该段具有超过一个分片的文档总数的 3% 的文档数,则按每个段进行缓存。由于缓存是按段划分的,因此合并段可使缓存的查询无效。

3.1.3 节点查询缓存配置

说一下静态配置(static)和 动态配置 (dynamic)配置的本质区别:

  • 静态配置:只能在配置文件配置,重启后生效。
  • 动态配置:可以通过命令行(更新 setting)配置,配置后无需重启即刻生效。

配置1:indices.queries.cache.size

  • 静态配置,需要在集群的每个数据节点配置。
  • 含义:控制 filter 缓存的堆内存大小。
  • 接受百分比值(例如5%)或精确值(例如 512mb)。
  • 默认:为10%。

配置2:index.queries.cache.enabled

  • 静态配置,针对每个索引的配置。
  • 含义:控制是否启用节点查询缓存。
  • 设置时机:只能在创建索引或者关闭索引(close)时设置。
  • 可设置:true 或者 false。
  • 默认:true。关闭缓存举例:
代码语言:javascript复制
PUT my_index_0003
{
  "settings": {
    "index.queries.cache.enabled": false
  }
}

3.2 分片请求缓存(Shard request cache)

当对一个索引或多个索引运行搜索请求时,每个涉及的分片都会在本地执行搜索并将其本地结果返回到协调节点,协调节点将这些分片级结果合并为一个“全局”结果集。

分片级请求缓存在每个分片上缓存本地结果,这使得频繁使用的搜索请求几乎立即返回结果。分片请求缓存非常适合日志用例场景,在这种情况下,数据不会在旧索引上更新,并且可以将常规聚合保留在高速缓存中以供重用。

默认情况下:

  • 请求缓存将仅缓存 size = 0 的搜索请求的结果,因此将不缓存hits,但将缓存hits.total,aggregations(聚合)和suggestions。
  • 大多数使用 now 的查询无法缓存。
3.2.1 分片请求缓存失效

刷新间隔(refresh_interval)越长,缓存的条目将保持有效的时间越长。如果缓存已满,将驱逐最近最少使用的缓存。

可以使用 clear_cache API手动使缓存过期,举例如下:

代码语言:javascript复制
POST /kimchy,elasticsearch/_cache/clear?request=true
3.2.2 启停分片请求缓存
  • 设置索引时默认停用缓存
代码语言:javascript复制
PUT my_index
{
  "settings": {
    "index.requests.cache.enable": false
  }
}
  • 更新索引,启用缓存
代码语言:javascript复制
PUT /my_index/_settings
{
  "index.requests.cache.enable": true
}
  • 查询时,设置分片请求缓存

如下的设置会覆盖索引级别的缓存设置。

代码语言:javascript复制
GET /my_index/_search?request_cache=true
{
  "size": 0,
  "aggs": {
    "popular_colors": {
      "terms": {
        "field": "colors"
      }
    }
  }
}

注意:

第一:如果你的查询使用结果有不确定的脚本(例如,使用随机函数或引用当前时间),则应将request_cache标志设置为false以禁用该请求的缓存。

第二:即使在索引设置中启用了请求缓存,也不会缓存大小大于0(size > 0)的请求。要缓存这些请求,您将需要使用 query-string 参数(详见官方文档)。

3.2.3 缓存设置

缓存是在节点级别进行管理的,默认最大大小为堆的1%。可以使用以下命令在config / elasticsearch.yml 文件中进行更改:

代码语言:javascript复制
indices.requests.cache.size: 2%

此外,您可以使用 index.requests.cache.expire 设置为缓存的结果指定TTL,但是没有理由这样做(提供此设置仅出于完整性考虑)。

请记住,刷新索引后(refreshed),旧的结果将自动失效。

3.2.4 缓存分片请求监控
代码语言:javascript复制
GET /_stats/request_cache?human

GET /_nodes/stats/indices/request_cache?human

3.3 Field data 缓存

Field data 缓存包含 field data 和 global ordinals,它们均用于支持某些字段类型上的聚合。由于这些都是堆上的数据结构,因此监视缓存的使用非常重要。

  • field data 咱们之前文章分析过:Elasticsearch 内部数据结构深度解读
  • global ordinals 可以简单理解为:预热全局序号,全局序号可以理解为:一种数据结构,用户 keyword 字段的 terms 聚合等用途。

Field data 缓存的构建成本很高,因此默认行为是将缓存加载到内存中。默认的缓存大小是无限的,这将导致缓存高速增长直到达到field data断路器设置的限制。

如果设置了 field data 缓存大小限制,同样的,缓存将开始清除缓存中最新最少更新的数据。此设置可以自动避开断路器限制,但需要根据需要重建缓存。

如果达到 field data 断路器限制,Elasticsearch 底层将阻止进一步增加缓存大小的请求。在这种情况下,你应该手动清除缓存。

这里要扩展两个Field data 断路器 配置:

参数1:indices.breaker.fielddata.limit

  • 动态参数。
  • 缺省值:堆内存40%。

参数2:indices.breaker.fielddata.overhead

  • 估计值常数,默认:1.03
3.3.1 Field data 缓存设置
  • 设置参数:indices.fielddata.cache.size。
  • 参数类型:静态参数。
  • 参数含义:field data 缓存的最大值。
  • 参数举例: 1)百分比,如:38%,代表:堆内存38%。

2)固定值,如:12 GB。

  • 缺省值:无
  • 设置举例:需要在:elasticsearch.yml 中设置,重启后生效,注意手动设置大小要小于断路器大小或者比例值。
3.3.2 Field data 缓存监控

以下两种方法可用于监控:Field data 缓存及 断路器使用情况。

  • Field data 缓存监控方法 1:
代码语言:javascript复制
GET /_nodes/stats
  • Field data 缓存监控方法 2:
代码语言:javascript复制
GET /_cat/fielddata

4、查询与清理缓存

4.0 查询缓存

全局查看缓存方法

代码语言:javascript复制
GET _cat/nodes?v&h=id,queryCacheMemory,queryCacheEvictions,requestCacheMemory,requestCacheHitCount,requestCacheMissCount,flushTotal,flushTotalTime

4.1 清理节点查询缓存

代码语言:javascript复制
POST /twitter/_cache/clear?query=true

4.2 清理 request 请求缓存

代码语言:javascript复制
POST /twitter/_cache/clear?request=true    

4.3 清理 field data 缓存

代码语言:javascript复制
POST /twitter/_cache/clear?fielddata=true 

4.4 指定索引清理缓存

代码语言:javascript复制
POST /kimchy,elasticsearch/_cache/clear

4.5 清理全部的缓存

代码语言:javascript复制
POST /_cache/clear

5、Elasticsearch 三种缓存应用场景

缓存类型

缓存内容

节点请求缓存

缓存可维护在 filter 上下文中使用的查询结果。

分片请求缓存

缓存 size = 0 时频繁使用的查询的结果,尤其是聚合的结果。

字段请求缓存 (Field data)

用于排序和支持某些字段类型上的聚合。

6、小结

读到这里,开头的问题的答案自然得出。

特将缓存使用注意事项说明如下:

  • 将聚合操作与“常规”查询处理分开。

原因:避免聚合随着用户的翻页(查询)重新计算。

  • 区分 filter 过滤器 和 match 匹配子句。

第一:通用 filter 过滤器具有很高的可缓存性,并且计算迅速;

第二:基于评分的 query 是相比 filter 更为昂贵的查询,并且难以缓存。

  • 在评分之前,使用可重复使用的过滤器(filters)来缩小结果集的范围。使用scripted fields进行评分,但不要使用过滤器。
  • Filters 过滤器或多或少地按顺序执行。ES 内部进行了一些查询重写,但通常将廉价的过滤器(执行快)放在首位,将较昂贵的过滤器(执行慢)放在第二位。
  • 如果必须按时间戳过滤,请使用粗粒度,以确保查询值改动小。

参考:

1、官方文档

2、https://opster.com/elasticsearch-glossary/elasticsearch-cache/

3、https://opensourceconnections.com


0 人点赞