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

ES内存配置

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

0x01 缘由

产品在生成环境中运行时,总是发现ES对内存的消耗较多,为了使服务器稳定运行,得适量控制ES对内存的消耗。
0x02 转载于 http://blog.csdn.net/hereiskxm/article/details/46744985

1. 产生 Data too large 异常

今早运行查询时,ES返回了如下报错:

{    "error": "... CircuitBreakingException[[FIELDDATA] Data too large, data for [proccessDate] would be larger than limit of [10307921510/9.5gb]]; }]",    "status": 500 }

再尝试其他查询也是如此。经排查,原来是ES默认的缓存设置让缓存区只进不出引起的,具体分析一下。


2. ES 缓存区概述

缓存区

首先简单描述一下ES的缓存机制。ES在查询时,会将索引数据缓存在内存(JVM)中: 

上图是 ES 的 JVM Heap 中的状况,可以看到有两条界限:驱逐线 和 断路器。当缓存数据到达驱逐线时,会自动驱逐掉部分数据,把缓存保持在安全的范围内。当用户准备执行某个查询操作时,断路器就起作用了,缓存数据+当前查询需要缓存的数据量到达断路器限制时,会返回 Data too large 错误,阻止用户进行这个查询操作。ES 把缓存数据分成两类,FieldData 和其他数据,我们接下来详细看 FieldData,它是造成我们这次异常的“元凶”。


3. FieldData

ES 配置中提到的 FieldData 指的是字段数据。当排序(sort),统计(aggs)时,ES 把涉及到的字段数据全部读取到内存(JVM Heap)中进行操作。相当于进行了数据缓存,提升查询效率。


3.1 监控 FieldData

仔细监控 fielddata 使用了多少内存以及是否有数据被驱逐是非常重要的。

Fielddata 缓存使用可以通过下面的方式来监控:

对于单个索引使用 {ref}indices-stats.html[indices-stats API]:

 GET /_stats/fielddata?fields=*

对于单个节点使用 {ref}cluster-nodes-stats.html[nodes-stats API]:

GET /_nodes/stats/indices/fielddata?fields=*

或者甚至单个节点单个索引

GET /_nodes/stats/indices/fielddata?level=indices&fields=*

通过设置 ?fields=* 内存使用按照每个字段分解了.

单节点 FieldData 监控

在产生 Data too large 异常时,对集群 FieldData 监控的返回结果如下: 
 
可以看到memory_size_in_bytes用到了整个 JVM 内存的 60%(可用上限),而evictions(驱逐)为 0。且经过一段时间观察,字段所占内存大小都没有变化。由此推断,当下的缓存处于无法有效驱逐的状态。


3.2 Cache 配置

关于 FieldData 的配置在 elasticsearch.yml 中,也可以通过调用 setting 接口来修改。API 文档里的介绍如下:

SettingDescription
indices.fielddata.cache.sizeThe max size of the field data cache, eg 30% of node heap space, or an absolute value, eg 12GB. Defaults to unbounded.
indices.fielddata.cache.expire[experimental] This functionality is experimental and may be changed or removed completely in a future release. A time based setting that expires field data after a certain time of inactivity. Defaults to -1. For example, can be set to 5m for a 5 minute expiry.

简而言之: 
indices.fielddata.cache.size 配置 fieldData 的 Cache 大小,可以配百分比也可以配一个准确的数值。cache 到达约定的内存大小时会自动清理,驱逐一部分 FieldData 数据以便容纳新数据。默认值为unbounded无限。 
indices.fielddata.cache.expire用于约定多久没有访问到的数据会被驱逐,默认值为-1,即无限。expire 配置不推荐使用,按时间驱逐数据会大量消耗性能。而且这个设置在不久之后的版本中将会废弃。

看来,Data too large 异常就是由于 fielddata.cache 的默认值为unbounded导致的了。


3.3 FieldData 格式

除了缓存取大小之外,我们还可以控制字段数据缓存到内存中的格式。

在 mapping 中,我们可以这样设置:

{    "tag": {        "type":      "string",        "fielddata": {            "format": "fst"        }    }}

对于 String 类型,format 有以下几种: 
paged_bytes (默认)

使用大量的内存来存储这个字段的 terms 和索引。

fst

用`FST`的形式来存储 terms。这在 terms 有较多共同前缀的情况下可以节约使用的内存,但访问速度上比 paged_bytes 要慢。

doc_values

fieldData 始终存放在 disk 中,不加载进内存。访问速度最慢且只有在 index:no/not_analyzed 的情况适用。

对于数字和地理数据也有可选的 format,但相对 String 更为简单,具体可在 api 中查看。 
从上面我们可以得知一个信息:我们除了配置缓存区大小以外,还可以对不是特别重要却量很大的 String 类型字段选择使用fst缓存类型来压缩大小。


4. 断路器

fieldData 的缓存配置中,有一个点会引起我们的疑问:fielddata 的大小是在数据被加载之后才校验的。假如下一个查询准备加载进来的 fieldData 让缓存区超过可用堆大小会发生什么?很遗憾的是,它将产生一个 OOM 异常。 
断路器就是用来控制 cache 加载的,它预估当前查询申请使用内存的量,并加以限制。断路器的配置如下:

indices.breaker.fielddata.limit

这个 fielddata 断路器限制 fielddata 的大小,默认情况下为堆大小的 60%。

indices.breaker.request.limit

这个 request 断路器估算完成查询的其他部分要求的结构的大小, 默认情况下限制它们到堆大小的 40%。

indices.breaker.total.limit

这个 total 断路器封装了 request 和 fielddata 断路器去确保默认情况下这 2 个部分使用的总内存不超过堆大小的 70%。

断路器限制可以通过文件 config/elasticsearch.yml 指定,也可以在集群上动态更新:

PUT /_cluster/settings{  "persistent" : {    "indices.breaker.fielddata.limit" : 40% (1)  }}

当缓存区大小到达断路器所配置的大小时会发生什么事呢?答案是:会返回开头我们说的 Data too large 异常。这个设定是希望引起用户对 ES 服务的反思,我们的配置有问题吗?是不是查询语句的形式不对,一条查询语句需要使用这么多缓存吗?

5. 总结


  1. 这次 Data too large 异常是 ES 默认配置的一个坑,我们没有配置 indices.fielddata.cache.size,它就不回收缓存了。缓存到达限制大小,无法往里插入数据。个人感觉这个默认配置不友好,不知 ES 是否在未来版本有所改进。
  2. 当前 fieldData 缓存区大小 < indices.fielddata.cache.size 
    当前 fieldData 缓存区大小+下一个查询加载进来的 fieldData < indices.breaker.fielddata.limit 
    fielddata.limit 的配置需要比 fielddata.cache.size 稍大。而 fieldData 缓存到达 fielddata.cache.size 的时候就会启动自动清理机制。expire 配置不建议使用。

  3. indices.breaker.request.limit 限制查询的其他部分需要用的内存大小。indices.breaker.total.limit 限制总(fieldData+其他部分)大小。

  4. 创建 mapping 时,可以设置 fieldData format 控制缓存数据格式。

http://blog.csdn.net/yinchunxiang/article/details/39011297 
https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata-formats.html 
https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-fielddata.html#index-modules-fielddata


露水湾 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:ES内存配置
喜欢 (2)
[]
分享 (0)
关于作者:
发表我的评论
取消评论

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

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

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. 有用
    frankietong22020-10-23 15:42 回复 Windows 10 | Chrome 86.0.4240.75