Elasticsearch搜索引擎之缓存:Request Cache、Query Cache、Fielddata Cache
ElasticSearch 查询需要占用 CPU、内存资源,在复杂业务场景,会出现慢查询,需要花费大量的时间。为了提高系统的性能,除了增加集群硬件配置这种成本高昂的开销外,还可以使用 ES 的缓存,下面我们就介绍几种 ES 中常用的缓存。一、Request cache:1、什么是 Request cache:Request Cache,全称是 Shard Request Cache,即分片级请求缓
ElasticSearch 查询需要占用 CPU、内存资源,在复杂业务场景,会出现慢查询,需要花费大量的时间。为了提高系统的性能,除了增加集群硬件配置这种成本高昂的开销外,还可以使用 ES 的缓存,下面我们就介绍几种 ES 中常用的缓存。
一、Request cache:
1、什么是 Request cache:
Request Cache,全称是 Shard Request Cache,即分片级请求缓存。当对一个或多个索引发送搜索请求时,搜索请求首先会发送到ES集群中的某个节点,称之为协调节点;协调节点会把该搜索请求分发给其他节点并在相应分片上执行搜索操作,我们把分片上的执行结果称为“本地结果集”,之后,分片再将执行结果返回给协调节点;协调节点获得所有分片的本地结果集之后,合并成最终的结果并返回给客户端。
By default, the requests cache will only cache the results of search requests where size=0, so it will not cache hits, but it will cache hits.total, aggregations, and suggestions.Most queries that use now (see Date Mathedit) cannot be cached.
Request Cache 在每个分片上缓存了本地结果集,这使得频繁使用的搜索请求几乎立即返回结果。默认情况下只会缓存查询中参数 size=0 的搜索请求的结果,因此将不会缓存hits,但会缓存 hits.total,aggregations(聚合) 和 suggestions。所以,request cache 分片请求缓存非常适合日志用例场景,在这种情况下,数据不会在旧索引上更新,并且可以将常规聚合保留在高速缓存中以供重用。
2、request cache 缓存的失效:
ES 能够保证在使用与不使用 Request Cache 情况下的搜索结果一致,那 ES 是如何保证的呢?这就要通过 Request Cache 的失效机制来了解啦。
Request Cache 缓存失效是自动的,当索引 refresh 时就会失效,也就是说在默认情况下, Request Cache 是每1秒钟失效一次,但需要注意的是,只有在分片的数据实际上发生了变化时,刷新分片缓存才会失效。也就是说当一个文档被索引 到 该文档变成Searchable的这段时间内,不管是否有请求命中缓存该文档都不会被返回。
所以我们可以通过 index.refresh_interval 参数来设置 refresh 的刷新时间间隔,刷新间隔越长,缓存的数据越多,当缓存不够的时候,将使用LRU最近最少使用策略删除缓存数据。
当然,我们也可以手动设置参数 indices.request.cache.expire 指定失效时间(单位为分钟),但是基本上我们没必要去这样做,因为缓存在每次索引 refresh 时都会自动失效。
最后,我们也可以通过 API 手动清除 Request Cache,使用方式如下:
curl -XPOST '索引的IP:端口/索引名/_cache/clear?request_cache=true'
3、request cache 的使用与设置:
3.1、request cache 的使用:
默认情况下,Request Cache 是关闭的,我们可以在创建新的索引时启用,例如:
curl -XPUT 服务器IP:端口/索引名 -d
'{
"settings": {
"index.requests.cache.enable": true
}
}'
也可以通过动态参数配置来进行设置:
curl -XPUT 服务器IP:端口/索引名/_settings -d
'{
"index.requests.cache.enable": true
}'
开启缓存后,需要在搜索请求中加上 request_cache=true 参数,才能使查询请求被缓存,比如:
curl -XGET '服务器IP:端口/索引名/_search?request_cache=true&pretty' -H 'Content-Type: application/json' -d
'{
"size": 0,
"aggs": {
"popular_colors": {
"terms": {
"field": "colors"
}
}
}
}'
两个注意事项:
(1)第一:参数 size:0 必须强制指定才能被缓存,否则请求是不会缓存的,即使手动的设置request_cache=true
(2)第二:在使用 script 脚本执行查询时,由于脚本的执行结果是不确定的(比如使用 random 函数或使用了当前时间作为参数),一定要指定 request_cache=false 禁用 Request Cache 缓存。
3.2、request cache 的设置:
Request Cache 作用域为 Node,在 Node 中的 Shard 共享这个Cache空间。默认最大大小为 JVM堆内存的1%。可以使用以下命令在 config / elasticsearch.yml 文件中进行更改:
indices.requests.cache.size: 1%
Request Cache 是以查询的整个DSL语句做为key的,所以如果要命中缓存,那么查询生成的DSL一定要一样,即使修改了一个字符或者条件顺序,都不能利用缓存,需要重新生成Cache。
3.3、request cache 大小的查看方式:
GET /_stats/request_cache?human
GET /_nodes/stats/indices/request_cache?human
二、QueryCache:
1、什么是 Query Cache:
Query Cache,也称为 Filter Cache,就是对查询中包含的 Filter 过滤器的执行结果进行缓存,缓存在节点查询缓存中,以便快速查找。每个节点都有一个所有分片共享的查询缓存。当缓存空间存满时,使用 LRU 策略清理最近最少使用的查询结果,以腾出空间存放新结果数据。
ES 在 5.1.1 版本中移除了 term query 的缓存,因为 term query 和 filter query 二者查询时间相差不多。
默认情况下,节点查询缓存最多可容纳10000个查询,最多占总堆空间的10%,为了确定查询是否符合缓存条件,Elasticsearch 维护查询历史记录以跟踪事件的发生。但是,当 segment 的文档数量小于 10000 或者 小于分片文档总数的 3% 时,该查询是不会被缓存的。
由于缓存是按段划分的,因此合并段可使缓存的查询无效,同时,只有对频繁访问的请求才会使用查询缓存,因为查询缓存是基于 bitmap 的,而建立 bitmap 的过程需要一定的时间。
2、Query Cache 相关参数配置:
(1)index.queries.cache.enabled:控制是否启用节点查询缓存,可以设置 true(默认) 或者 false。该设置只能在创建索引或者索引关闭(close)时设置:
PUT my_index_01{ "settings": { "index.queries.cache.enabled": false }}
(2)indices.queries.cache.size:设置查询缓存的对堆内存大小,默认10%,可设置成百分比,也可设置成具体值,如 512m。
三、Fielddata Cache:
1、什么是 Fielddata Cache:
ES 的快速搜索特性主要依赖于倒排索引这种数据结构,但如果仅仅依靠倒排索引是很难在查询中做到排序和统计的,因为它并不像关系型数据库那样采用“列式存储”,而是基于一个 “term” 到 “document” 的倒排。这种情况下,如果需要做数据聚合和排序,就需要将倒排索引的数据读取出来,重新组织成一个数组缓存,也就是从倒排索引中生成出来的要自己维护这段Cache, 之后才能够高效的做排序和聚合计算。
为了解决上面的问题,就出现了 Fielddata Cache 字段数据缓存,它主要用于字段的排序和聚合,将所有的字段值加载到内存中,以便提供基于文档快速访问这些值。当第一次在某个分词的字段上执行聚合、排序或通过脚本访问的时候就会触发该字段 Fielddata Cache 的加载,这种缓存是 segment 级别的,当有新的 segment 打开时旧的缓存不会重新加载,而是直接把新的 segment 对应的 Fielddata Cache 加载到内存。
因为是基于 segment 级别的,所以 Fielddata Cache 失效和 Node Query Cache 失效机制相同,当 segment 被合并后,才会失效。
Fielddata Cache 的构建成本很高,一旦 Fielddata 被加载到内存,那么在该 Fielddata Cache 对应的 Segment 生命周期范围内都会驻留在堆内存中,也就是说当触发段合并时会导致合并后的更大段的 Fielddata Cache 加载。同时,由于 Fielddata Cache 默认缓存大小是无限的,这将导致缓存高速增长直到达到 field data 断路器设置的限制。特别是当加载“高基数”的分词字段时(那些分词后存在大量不同词的字段),针对这种字段的聚合排序其实是非常没有意义的,我们更多的要去考虑是否能用 not_analyzed 代替。如果设置了 fielddata cache 大小限制,缓存就会清除缓存中最新最少更新的数据。此设置可以避开 field data 断路器限制,但需要根据需要重建缓存;如果达到 field data 断路器限制,Elasticsearch 底层将阻止进一步增加缓存大小的请求。
由于 Fielddata Cache 是存放在堆内存中,在海量数据聚合的时候,生成的这些 fielddata 可能堆内存放不下,从而引起性能问题,甚至JVM OOM。所以 Elasticsearch2.0 开始,在非 text 字段开启 doc_values,基于 doc_values 做排序和聚合,可以减少对 FielddataCache 的依赖,减少内存消耗,因为 doc_values 在使用时不需要全部载入内存,可以减少节点 OOM 的概率。由于 doc_values 的特性性能上也不会有多少损失,doc_values 是一种正向索引结构以顺序预读的方式进行获取,所以随机获取就很慢了。在 5.0 开始,text 字段默认关闭了 Fielddata 功能, Fielddata Cache 应当只用于 global ordinals。
对 doc_values 的介绍与使用感兴趣的读者可以移步这篇文章:https://blog.csdn.net/a745233700/article/details/117915118
2、 Fielddata Cache 参数配置:
(1)开启与关闭 Fielddate Cache:
默认情况下Fielddate Cache是默认开启的,我们可以通过下面的设置来关闭,关闭后就无法对分词字段执行聚合、排序操作了。
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"text": {
"type": "string",
"fielddata": {
"format": "disabled"
}
}
}
}
}
}
(2)indices.fielddata.cache.size:设置字段数据缓存的最大值,通过百分比 30% 或者具体值 12GB 来设置,默认无限制。
(3)indices.breaker.fielddata.limit:此参数设置 Fielddata 断路器限制大小(公式:预计算内存 + 现有内存 <= 断路器设置内存限制),默认是60%JVM堆内存,当查询尝试加载更多数据到内存时会抛异常(以此来阻止JVM OOM发生)
(4)indices.breaker.fielddata.overhead:一个常数表示内存预估值系数,默认1.03,比如预计算加载100M数据,那么100*1.03=103M会用103M作为参数计算是否超过断路器设置的最大值。
参考文章:Elasticsearch2.x 三种缓存介绍:Query Cache、Request Cache、Fielddata Cache_飞奔的代码-CSDN博客_es query_cache
更多推荐
所有评论(0)