ES 内存管理分析

刚接触ES,研究了下ES内存管理,参考了一些文章,整理了一篇文章,方便自己记忆。

命令 GET _cat/nodes?help 列出所有node, 并展示node所在机器的运行状态信息,help可显示帮助信息

1
GET _cat/nodes?h=name,hp,hm,rp,rm,qcm,rcm,fm,sm&v

解析下上面参数的意义

信息如下:

以红框里的node为例, 内存占用 = (8.6 gb)qcm + (1gb) rcm + (0.35gb) fm + (2.2 gb)sm,大约12 gb。

内存占用有2种,一种是ES可以管理的 on-heap 内存,另一种是由Lucene管理的 off-heap 内存。

On Heap

根据缓存占用的作用,可以分为以下五种缓存:节点查询缓存、分片请求缓存、Fielddata Cache、Segments FST Cache、Indexing Buffer(无参数可看占用的大小)。

节点查询缓存

Node Query Cache (node 级别的 filter 过滤器结果缓存), 每个节点有一个,被所有 shard 共享,filter query 结果要么是 yes 要么是no,不涉及 scores 的计算。使用LRU淘汰策略,内存无法被GC。
集群中每个节点都要配置,indices.queries.cache.size: 10%,默认为 10%。index.queries.cache.enabled 用来控制具体索引是否启用缓存,默认是开启的。属于 index 级别配置,只用在索引创建时候或者关闭的索引上设置。

分片请求缓存

Shard Request Cache (shard 级别的 query result 缓存)每个 shard 一个,默认情况下该缓存只缓存 request 的结果 size 为0的查询。所以该缓存不会缓存hits,但却会缓存 hits.total, aggregations 和 suggestions,默认值:indices.requests.cache.size: 1%

Fielddata Cache

Field Data Cache (OLAP场景,用于排序、聚合使用的字段的缓存)。对于Text类型的字段,如果要对其进行聚合和排序,则需要打开字段的Fileddata属性。Fielddata 是延迟加载。如果你从来没有聚合一个分析字符串,就不会加载 fielddata 到内存中。如果没有足够的内存保存 fielddata 时,Elastisearch会不断地从磁盘加载数据到内存,并剔除掉旧的内存数据。剔除操作会造成严重的磁盘I/O,并且引发大量的GC,会严重影响Elastisearch的性能。

通过参数 indices.fielddata.cache.size: 30% 配置,默认情况下Fielddata会不断占用内存,无上限,直到它触发了 fielddata circuit breaker。

fielddata circuit breaker会根据查询条件评估这次查询会使用多少内存,从而计算加载这部分内存之后,Field Data Cache所占用的内存是否会超过 indices.breaker.fielddata.limit。如果超过这个值,就会触发fielddata circuit breaker,abort这次查询并且抛出异常,防止OOM。

indices.breaker.fielddata.limit: 60% (默认heap的60%) ,如果设置了indices.fielddata.cache.size,当达到size时,cache会剔除旧的fielddata。注意,indices.breaker.fielddata.limit 必须大于 indices.fielddata.cache.size,否则只会触发fielddata circuit breaker,而不会剔除旧的fielddata。

Segments FST Cache

Segments Cache(segments FST数据的缓存),为了加速查询,FST 永驻堆内内存,无法被 GC 回收。该部分内存无法设置大小,减少data node上的segment memory占用,有三种方法:

  • 删除不用的索引。
  • 关闭索引(文件仍然存在于磁盘,只是释放掉内存),需要的时候可重新打开。
  • 定期对不再更新的索引做force merge

解释下FST

ES 底层存储采用 Lucene(搜索引擎),写入时会根据原始数据的内容,分词,然后生成倒排索引。查询时先通过查询倒排索引找到数据地址(DocID),再读取原始数据(行存数据、列存数据)。但由于 Lucene 会为原始数据中的每个词都生成倒排索引,数据量较大。所以倒排索引对应的倒排表被存放在磁盘上。这样如果每次查询都直接读取磁盘上的倒排表,再查询目标关键词,会有很多次磁盘 IO,严重影响查询性能。为了解磁盘 IO 问题,Lucene 引入排索引的二级索引 FST [Finite State Transducer] 。原理上可以理解为前缀树,加速查询。

Indexing Buffer

Indexing Buffer 索引写入缓冲区,用于存储新写入的文档,当其被填满时,缓冲区中的文档被写入磁盘中的 segments 中。节点上所有 shard 共享。这部分空间是可以被GC的,缓冲区默认大小 10%。

Off-heap

上面的提到的内存都是JVM管理的,ES能控制,即On-heap内存,ES还有Off-heap内存,由Lucene管理,负责缓存倒排索引(Segment Memory)。Lucene 中的倒排索引 segments 存储在文件中,为提高访问速度,都会把它加载到内存中,从而提高 Lucene 性能。

总结

缓存的清除

清除全部的缓存:

1
POST /_cache/clear

清除特定索引的缓存:

1
2
POST /my_index/_cache/clear
POST /my_index1,my_index2/_cache/clear

通过设置fielddata, query, request参数为true来清除特定类型的缓存清除特定类型缓存:

1
2
3
POST /my-index/_cache/clear?fielddata=true
POST /my-index/_cache/clear?query=true
POST /my-index/_cache/clear?request=true

通过上面分析,ES内存管理主要是谨慎对待unbounded的内存。unbounded内存是不可控的,会占用大量的 heap (Field Data Cache)或者 off heap(segments会长期占用内存,其初衷就是利用OS的cache提升性能。只有在Merge之后,才会释放掉标记为Delete的segments,释放部分内存),从而会导致Elasticsearch OOM。

查看Cache对应的类

一个小技巧,测试环境对ES jmap下,之后MAT分析,选择dominator_tree,之后group by class,然后模糊匹配 Cache 关键字,之后可以看到一些关键类,比如IndicesRequestCache和IndicesFieldDataCache,而 FST Cache我还没有找到对应的类,还需要继续熟悉下代码。

参考链接:

https://blog.csdn.net/microGP/article/details/106949969

目录

  1. 1. On Heap
    1. 1.1. 节点查询缓存
    2. 1.2. 分片请求缓存
    3. 1.3. Fielddata Cache
    4. 1.4. Segments FST Cache
      1. 1.4.1. 解释下FST
    5. 1.5. Indexing Buffer
  2. 2. Off-heap
  3. 3. 总结
  4. 4. 缓存的清除
  5. 5. 查看Cache对应的类
  6. 6. 参考链接: