故障描述:
某天对Es做多次查询请求,发现Es集群经常挂掉,无法响应。
定位问题:
我们的Es在之前较长时间内未出现故障,所以可认为时近期内的变更导致的。最近我们新运行了一个bulk index程序生产Es数据。
1.index方面:首先我们怀疑可能是这个bulk index程序可能触发Es某些问题。但是关闭该程序并重启Es集群,该故障仍然会发生。
2.search方面:故障是发生在Es数据快速增长之后,那么有很大的可能是数据量增长之后,某个search请求触发es问题。因为我们的系统构建于Es之上,且较为复杂,无法很快的定位是哪次请求导致Es故障。很难从这里下手定位问题。但是query/filter,aggs几类请求中,最可能的是不合理aggs请求导致的。
确认问题:
查看日志,在当天的日志中,发现出现故障时Es在频繁的Full GC
甚至可能会OOM
Es本质上是一个java程序,在进行GC时整个程序无法响应。长时间GC时,集群其它节点在无法获得某个节点的心跳响应时,集群会认为这个这个节点退出了集群。可是从其它节点日志发现这一点:
丢失整个节点的分片数据后,由于Es高可用自平衡机制,集群开始平衡分片等任务。集群长时间处于不健康状态。
现在可以确定是某次查询请求导致节点内存被大量使用,触发Full GC(Es默认会在内存使用75%时发生FullGc -XX:CMSInitiatingOccupancyFraction=75),而该内存又不能被GC回收掉,接着频繁GC。甚至可能直接OOM。结果该节点长时间无法响应外部请求。
问题请求:
经过不断排查,发现可疑请求search dsl为:
该聚合请求将数据按分钟分桶,每个桶计算top 1的文档,当时间范围横跨较大时,会相当吃内存。 该请求横跨一天的数据,数据量为0.75M,并且同时并发7次类似请求,数据内容不一致(_msearch)。经测试如果请求一个月数据必然复现故障问题。
问题原因:
ElasticSearch在收集文档用于返回时非常的耗内存,所以才有from+size<=10000的默认设定。但是在bucket agg的子agg使用top_hits agg时,可以突破这个限制(ElasticSearch没有这个限制)。所以当bucket数量非常大时,非常容易导致OOM。假设请求10天的数据,每秒分桶,每个桶内取top 10,那么这个数量级为10*24*60*60*10=8640000,即你可以取到864万条数据,再大的内存也无法完成这个计算。而且这只是一个分片shard的数据,而协调节点(请求的那个节点)需要将所有分片节点的数据集合到一起再做reduce,内存使用是其它节点的倍数,更加容易OOM。我们这次故障就是发生在协调节点reduce数据时。
解决问题:
对于上面的请求,业务上实际没有必要每个桶都取top1,所以完全可以将top1提升为父聚合的兄弟来避免这个问题。而对于无法改进的dsl,则应该将所有数据切片,比如按时间分片,多次请求来避免这个问题。
思考:
1.对于分桶数量太大的聚合请求,应该将所有数据切片,比如按时间分片,多次请求来避免内存OOM。
2.集群中应该有独立的协调节点,专门用于数据请求(node.master=false node.data=false),并给它们设置足够的内存。通过数据节点与协调节点分离,可以避免节点挂掉之后,导致整个集群不可用,或者长时间响应迟钝。
3.在探究Es的故障时,能实时看Es各项指标非常有用。比如,对于本文中的这种故障,能看到jvm的内存使用是非常有帮助的。所以最好能有一个对Es的专门的监控系统。如何监控Elasticsearch?
4.使用docvalue是解决该类问题的王道。es2.x版本,使用string类型保存字符串,string类型默认分词analyzed。分词的string是无法利用docvalue的。那么问题来了,怎么即想分词,又想利用docvalue呢:
PS:有人可能会说CMS垃圾回收在大内存情况下表现不如G1,但是Elasticsearch官方文档不建议使用官方文档
网友评论