• -------------------------------------------------------------
  • ====================================

Elasticsearch2.x 三种缓存介绍:Query Cache、Request Cache、Fielddata Cache

elasticsearch dewbay 5年前 (2019-04-12) 2552次浏览 已收录 0个评论 扫描二维码

一、Query Cache
Query Cache也称为 Filter Cache,顾名思义它的作用就是对一个查询中包含的过滤器执行结果进行缓存。
比如我们常用的 term,terms,range 过滤器都会在满足某种条件后被缓存,注意,这里的 bool 过滤器是不会被缓存的,但 bool 过滤器包含的子 query clause 会被缓存,我们可以用下面的命令来查询Query Cache的情况。
http://192.168.0.109:9200/_stats/query_cache?pretty&human
举个栗子,看下面的查询
{
“from”: 0,
“size”: 5,
“query”: {
“bool”: {
“filter”: {
“bool”: {
“must”: [
{
“term”: {
“productID”: “JODL-X-1937-#pV7”
}
},
{
“range”: {
“price”: {
“from”: 20,
“to”: null,
“include_lower”: true,
“include_upper”: true
}
}
}
]
}
}
}
}
}
上面有两个过滤器一个 Term 过滤器用来过滤 productID 为“JODL-X-1937-#pV7” 的产品,一个 range 过滤器用来过滤价格在 20 以上的产品,在这个例子中这两个过滤器执行的结果会分别作为一个 BitSet(位图)缓存,返回的查询结果则是这两个位图交集。
上面提到 Filter Cache 只会在满足某种条件下才会被缓存,至于是哪些条件这里就不介绍了,想了解的童鞋戳下面链接。
关于 Filter 执行流程及缓存原理 ,请参看此文:《Elasticsearch2.X Filter 执行流程及缓存原理》

二、Request Cache
当一个查询发送到 ES 集群的某个节点上时,这个节点会把该查询扩散到其他节点并在相应分片上执行,我们姑且把在分片上执行的结果叫“本地结果集“,这些本地结果集最终会汇集到最初请求到达的那个协调节点,这些“分片级”的结果集会合并成“全局”结果集返回给调用端。
Request Cache模块就是为了缓存这些“分片级”的本地结果集,但是目前只会缓存查询中参数 size=0 的请求,所以就不会缓存 hits 而是缓存 hits.total,aggregations 和 suggestions
缓存失效
Request Cache是非常智能的,它能够保证和在近实时搜索中的非缓存查询结果一致。这句话读起来很难懂,简单解释下。
我们都知道 ES 是一个“near real-time”(近实时)搜索引擎,为什么是近实时搜索呢,那是因为当我们向 ES 发送一个索引文档请求到这个文档变成 Searchable(可搜索)默认的时间是 1 秒,我们可以通过 index.refresh_interval 参数来设置刷新时间间隔,也就是说我们在执行一个搜索请求时实际上数据是有延迟的。回到刚才的问题,刚才那句话其实指的就是:ES 能保证在使用Request Cache的情况下的搜索结果和不使用Request Cache的近实时搜索结果相同,那 ES 是如何保证两者结果相同的呢?继续……

Request Cache缓存失效是自动的,当索引 refresh 时就会失效,也就是说在默认情况下 Request Cache 是每 1 秒钟失效一次(注意:分片在这段时间内确实有改变才会失效)。也就是说当一个文档被索引到该文档变成 Searchable 之前的这段时间内,不管是否有请求命中缓存该文档都不会被返回,正是是因为如此 ES 才能保证在使用 Request Cache 的情况下执行的搜索和在非缓存近实时搜索的结果一致。如果我们把索引刷新时间设置得越长那么缓存失效的时间越长,如果缓存被写满将采用 LRU 策略清除。当然我们也可以手动设置参数 indices.request.cache.expire 指定失效时间,但是基本上我们没必要去这样做,因为缓存在每次索引 refresh 时都会自动失效。
下面的命令可以手动清除缓存
curl -XPOST ‘localhost:9200/kimchy,elasticsearch/_cache/clear?request_cache=true’
缓存使用
在默认情况下 Request Cache 是关闭的,我们需要手动开启
curl -XPUT localhost:9200/my_index/_settings -d’
{ “index.requests.cache.enable”: true }

开启缓存后,我们需要在那些需要缓存的搜索请求上加上 request_cache=true 这个参数才能使我们的查询请求被缓存,比如:
curl ‘localhost:9200/my_index/_search?request_cache=true’ -d’
{
“size”: 0,
“aggs”: {
“popular_colors”: {
“terms”: {
“field”: “colors”
}
}
}
}

注意 1:上面的参数 size:0 非常重要必须强制指定才能被缓存,否则请求是不会缓存的。
注意 2(重要):在使用 script 执行查询时一定要指定 request_cache=false,因为脚本的执行结果是不确定的(比如使用 random 函数或使用了当前时间作为参数),这种情况下应该避免使用缓存
缓存的 Cache Key
对于 Request Cache 来说,它的 Cache Key 就是整个查询的 DSL 语句,所以如果要命中缓存查询生成的 DSL 一定要一样,这里的一样是指 DSL 这个字符串一样。只要有一个字符或者子查询的顺序变化都不会命中缓存。
通过下面的参数我们可以设置缓存的大小,默认情况下是 JVM 堆的 1%大小,当然我们也可以手动设置在elasticsearch.yml 文件里
indices.requests.cache.size: 1%
Request Cache 总结:

  1. Request Cache 是一个“分片级”的缓存
    2. Request Cache 是一个面向请求的缓存,缓存的 key 是查询 DSL 字符串
  2. Request Cache 默认没有开启,需要手动开启,且要缓存生效需要在请求参数上加上 request_cache=true 并把 size 设置为 0
  3. 缓存是自动失效的,失效时间就是索引的 refresh 时间(index.refresh_interval),在分片有改变的情况下默认是 1 秒失效一次
  4. 缓存的默认大小是 JVM 堆内存的 1%,可以通过参数 indices.request.cache.expire 手动设置

三、Fielddata
一谈到 Fielddata 我们不得不提到 doc_values,这两者的作用都是一样:能够让我们在 inverted index(倒排索引)的基础之上做 aggregation、sort 或者在查询中通过 script 访问 doc 属性,这里我们不讨论 doc_values,主要讲下 Fielddata,doc values 相关知识请戳:http://blog.csdn.net/chennanymy/article/details/52555055
想必大家都知道倒排索引这种结构,如果我们仅仅依靠倒排是很难在查询中做到排序和统计的,因为它并不是像关系型数据库那样采用“列式存储”,而是基于一个“词”到“文档”的倒排。
Fielddata 是专门针对分词的字段在 query-time(查询期间)的数据结构的缓存。当我们第一次在一个分词的字段上执行聚合、排序或通过脚本访问的时候就会触发该字段Fielddata Cache的加载,这种缓存是“segment”级别的,当有新的 segment 打开时旧的缓存不会重新加载,而是直接把新的 segement 对应的Fielddata Cache加载到内存。
加载Fielddata Cache是一个非常昂贵的操作,一旦 Fielddata 被加载到内存,那么在该Fielddata Cache对应的 Segement 生命周期范围内都会驻留在内存中。也就是说当段合并时会触发合并后更大段的Fielddata Cache加载。
Fielddata 会消耗大部分的 JVM 堆内存,特别是当加载“高基数”的分词字段时(那些分词后存在大量不同词的字段),针对这种字段的聚合排序其实是非常没有意义的,我们更多的要去考虑是否能用 not_analyzed 代替(这样就可以使用 doc_values 实现聚合、排序)。

默认情况下 Fielddate Cache 是默认开启的,我们可以通过下面的设置来关闭,关闭后就无法对分词字段执行聚合、排序操作了。
PUT my_index
{
“mappings”: {
“my_type”: {
“properties”: {
“text”: {
“type”: “string”,
“fielddata”: {
“format”: “disabled”
}
}
}
}
}
}
在 ES1.X 里面除了 string 类型其他类型也是使用 Fieldata 的,在 ES2.X 中除了分词的 String 类型字段,其他类型都使用 doc_values。
Fielddata 的加载方式有 3 种:
1.lazy: 懒加载是默认的加载方式,当第一次使用时加载
2.eager:预加载模式是当一个新的索引段变成 Searchable 之前会被加载
3.eager_global_ordinals:全局序数预加载模式,这种方式能生成一份全局序数表,可降低内存使用。
如下设置为 eager_global_ordinals
PUT my_index
{
“mappings”: {
“my_type”: {
“properties”: {
“text”: {
“type”: “string”,
“fielddata”: {
“loading”: “eager_global_ordinals”
}
}
}
}
}
}
Fielddata 也可指定满足某些条件的 term 才被加载进内存
1.通过词频加载
PUT my_index
{
“mappings”: {
“my_type”: {
“properties”: {
“tag”: {
“type”: “string”,
“fielddata”: {
“filter”: {
“frequency”: {
“min”: 0.001,
“max”: 0.1,
“min_segment_size”: 500
}
}
}
}
}
}
}
}
上面的设置表示词频在 0.001 到 0.1 之间的且段持有的文档数在 500 以上的 term 会被加载到内存。
2.通过正则表达式加载
PUT my_index
{
“mappings”: {
“my_type”: {
“properties”: {
“tweet”: {
“type”: “string”,
“analyzer”: “whitespace”,
“fielddata”: {
“filter”: {
“regex”: {
“pattern”: “^#.*”
}
}
}
}
}
}
}
}
上面的设置表示只有满足 pattern 的词才会被加载到内存。
Fielddata Cache设置
1.indices.fielddata.cache.size:此参数设置缓存大小(默认是不限制)。可设置百分数如 30%,或者数字 12GB
2.indices.breaker.fielddata.limit:此参数设置 Fielddata 断路器限制大小(公式:预计算内存 + 现有内存 <= 断路器设置内存限制),默认是 60%JVM 堆内存,当查询尝试加载更多数据到内存时会抛异常(以此来阻止 JVM OOM 发生)

3.indices.breaker.fielddata.overhead:一个常数表示内存预估值系数,默认 1.03,比如预计算加载 100M 数据,那么 100*1.03=103M 会用 103M 作为参数计算是否超过断路器设置的最大值。

作者:飞奔的代码
来源:CSDN
原文:https://blog.csdn.net/chennanymy/article/details/52504386
版权声明:本文为博主原创文章,转载请附上博文链接!


露水湾 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Elasticsearch2.x 三种缓存介绍:Query Cache、Request Cache、Fielddata Cache
喜欢 (1)
[]
分享 (0)
关于作者:
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址