背景
在使用elasticsearch时,如果使用了高基数的聚合查询,有可能导致内存溢出OOM,导致elasticsearch的节点问题,进一步引发该节点上的分片漂移,从而影响线上的查询问题。
原因
Elasticsearch在执行高基数查询时,会默认为每组数据生成一个桶,并且存放在内存中,当基数很大也就是分组数据的重复性不高时,就会生成很多的桶,从而占用大量的内存。
内存使用介绍
ES的JVM heap按使用场景分为可GC部分和常驻部分。 可GC部分内存会随着GC操作而被回收; 常驻部分不会被GC,通常使用LRU策略来进行淘汰; 内存占用情况如下图:
1.common space
包括了indexing buffer和其他ES运行需要的class。indexing buffer由indices.memory.index_buffer_size参数控制, 默认最大占用10%,当full up后,该部分数据被刷入磁盘对应的Segments中。这部分空间是可以被回收反复利用的。
2.queryCache
node级别的filter过滤器结果缓存,大小由indices.queries.cache.size 参数控制,默认10%。使用LRU淘汰策略。
3.requestCache
是shard级别的query result缓存,通常 only requests of size 0 such as aggregations, counts and suggestions will be cached。使用LRU淘汰策略。通过indices.requests.cache.size参数控制,默认1%。设置后整个NODE都生效。
4.fieldDataCache
针对text字段,没有docValues属性(相当于列存储),当对text类型字段进行sort,agg时,需要将对应的字段内容全部加载到内存,这部分数据就放在fieldDataCache。通过indices.fielddata.cache.size 参数限制大小,默认不限制。这种情况下,占用内存会逐渐增多,直到触发熔断;新数据无法加载。
5.segmentsMemory
缓存段信息,包括FST,Dimensional points for numeric range filters,Deleted documents bitset ,Doc values and stored fields codec formats等数据。这部分缓存是必须的,不能进行大小设置,通常跟index息息相关,close index、force merge均会释放部分空间。
断路器介绍及设置
ES通过断路器的设置,来保护集群内存不超过设定值,从而保证集群的稳定性。
1.indices.breaker.fielddata.limit
fielddata断路器默认设置堆的 40% 作为 fielddata 大小的上限。
2.indices.breaker.request.limit
request断路器估算需要完成其他请求部分的结构大小,例如创建一个聚合桶,默认限制是堆内存的 60%。它实际上是node level的一个统计值,统计的是这个结点上,各类查询聚合操作,需要申请的Bigarray的空间大小总和。 所以如果有一个聚合需要很大的空间,同时在执行的聚合可能也会被break掉。
3.indices.breaker.total.limit
父断路器,保证inflight、request(agg)和fielddata的使用不会使用超过堆内存的限制,默认70%。
4.network.breaker.inflight requests.limit
限制当前通过HTTP等进来的请求使用内存不能超过Node内存的指定值。这个内存主要是限制请求内容的长度。 默认100%。
5.script.context.$CONTEXT.max_compilations_rate
特定时间间隔内允许为给定上下文编译的唯一动态脚本数的限制。默认为75/5m,即每5分钟75次。
6.设置命令举例:
{
'error': {
'type': 'circuit_breaking_exception',
'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
'bytes_wanted': 123848638,
'bytes_limit': 123273216,
'durability': 'TRANSIENT'
},
'status': 429
}
效果演示
测试节点配置128MB的JVM运行,父熔断器设置为95%,即JVM使用达到117.5MB会触发熔断。然后不停的发出聚合请求,节点将返回code 429,如下:
{
'error': {
'type': 'circuit_breaking_exception',
'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
'bytes_wanted': 123848638,
'bytes_limit': 123273216,
'durability': 'TRANSIENT'
},
'status': 429
}