一、前情提要
1、version
es 6.4
jdk 8
2、 先说下背景,目前公司使用elk 作为日志收集报警一条龙服务,并且skywalking 的agent采集数据也进入相同的es集群,目前使用的表现为,随着数据量持续增大。kibana 有时候都打不开,skywalking查询更别提了,而且数据不全,发现很多数据存储失败。下面就来跟进排查下。
3、简单的看下架构图
概念解析:
- Node(节点):单个的装有Elasticsearch服务并且提供故障转移和扩展的服务器。
- Cluster(集群):一个集群就是由一个或多个node组织在一起,共同工作,共同分享整个数据具有负载均衡功能的集群。
- Document(文档): 一个文档是一个可被索引的基础信息单元。
- Index(索引):索引就是一个拥有几分相似特征的文档的集合。
- Type(类型):一个索引中,你可以定义一种或多种类型。
- Field(列): Field是Elasticsearch的最小单位,相当于数据的某一列。
- Shards(分片):Elasticsearch将索引分成若干份,每个部分就是一个shard。
- Replicas (复制):Replicas是索引一份或多份拷贝。
4、影响elasticsearch性能的因素
二、性能优化-内存篇
入手点很简单,直接看kibana监控如下:
如上图可以看出,监控图表不连续啊,居然出现很多断点情况,而且这只是一个node的监控,看整体监控的话会发现整个集群基本处于无法提供服务的状态(虽然是绿色的)。
通过JVM heap 会感觉出整个内存回收的频率不对劲啊最大,貌似一直在进行FULL GC 啊,马上查看下 gc.log
果不其然,一直在进行FULL GC,直接查看es jvm.options 配置
-Xms16g
-Xmx16g
-XX:CMSInitiatingOccupancyFraction=70
结合上图会发现16G 明显不够用吗,查看了下机器内存发现居然有128G ,啧啧,这么小气,才给es配置16g,好吧,整个简单,我来加大下不就够用了。
在加大这个内存的时候要注意一点,对于最大内存官方建议是整体内存的50%,但是最大不要超过32G ,这里我们设置30G,原因如下:
因为涉及到一个JVM OOPS的优化策略
- 堆小于4G,无需编/解码操作,JVM会使用低虚拟地址空间(low virutal address space,64位下模拟32位)
- 小于32G而大于4G,使用Zero Based Compressed OOPS
- 大于32G,不使用Compressed OOPS
然后继续优化,将我们的内存加大到30
-Xms30g
-Xmx30g
重启节点,如图:
诶,貌似内存q曲线正常了,非常不错嘛,很简单吗,然而并不是这样,随着时间推移,内存又发生了变化
发现FULL GC 的频率越来越快,而且老年代的内存基数越来越大,系统随着时间推移会恢复到频繁FULL GC 的状态,这么看内存中有数据滞留啊,翻看了下官方文档发现几个很重要的概念:
common space
包括了indexing buffer和其他ES运行需要的class。indexing buffer由indices.memory.index_buffer_size参数控制, 默认最大占用10%,当full up后,该部分数据被刷入磁盘对应的Segments中。这部分空间是可以被回收反复利用的。query cache
实例级别的,作用域是node,其按照子Query来确定是否被Cache。 Cache的结果是DocIdSet,可以简单理解为布隆过滤器request cache
shard级别的缓存,主要缓存size=0的请求,缓存hits total,以及aggs等信息fielddata cache
针对text字段,没有docValues属性(相当于列存储),当对text类型字段进行sort,agg时,需要将对应的字段内容全部加载到内存,这部分数据就放在fieldDataCache。通过indices.fielddata.cache.size 参数限制大小,默认不限制。这种情况下,占用内存会逐渐增多,直到触发熔断;新数据无法加载segment memory
缓存段信息,包括FST,Dimensional points for numeric range filters,Deleted documents bitset ,Doc values and stored fields codec formats等数据。这部分缓存是必须的,不能进行大小设置,通常跟index息息相关,close index、force merge均会释放部分空间。 可以通过命令
GET _cat/nodes?v&h=id,ip,port,r,ramPercent,ramCurrent,heapMax,heapCurrent,fielddataMemory,queryCacheMemory,requestCacheMemory,segmentsMemory
ES 的熔断策略,ES 本身有对内存的保护策略,以防止OOM
indices.breaker.fielddata.limit fielddata 断路器默认设置堆的 60% 作为 fielddata 大小的上限。
indices.breaker.request.limit request 断路器估算需要完成其他请求部分的结构大小,例如创建一个聚合桶,默认限制是堆内存的 60%。它实际上是node level的一个统计值,统计的是这个结点上,各类查询聚合操作,需要申请的Bigarray的空间大小总和。 所以如果有一个聚合需要很大的空间,同时在执行的聚合可能也会被break掉。
indices.breaker.total.limit 上面两个限制的总开关request(agg)和fielddata不会使用超过堆内存的 70%。
network.breaker.inflight requests.limit 限制当前通过HTTP等进来的请求使用内存不能超过Node内存的指定值。这个内存主要是限制请求内容的长度。 默认100%。
script.max_compilations_per_minute
限制script并发执行数,默认值为15。
来看图说话(图片来源于网络,ES的JVM heap按使用场景分为可GC部分和常驻部分。 可GC部分内存会随着GC操作而被回收; 常驻部分不会被GC,通常使用LRU策略来进行淘汰)
common space(可GC)
综上所述:
segment memory 这是没办法通过参数配置释放的,通常跟index息息相关,close index、force merge均会释放部分空间.
综上所述,需要保证 segment memory 和 可gc的空间 +固定空间比不超过100%。由于熔断器是按整个heap大小来计算的,所以如果segment memory 过大,仍然可能会导致OOM。
下面为设置其配置:
其实正常情况下es集群默认配置足够使用,这里我们控制下fielddata 的内存使用
indices.fielddata.cache.size: 20%
restart
su - elk -c "/data/secoo_program/elasticsearch-6.4.0/bin/elasticsearch -d"
优化内存后的使用情况
如图可看出,内存使用回收趋于平稳,再看下系统整体监控对比图
可以看出 优化前后效果明显,优化前系统基本处于FULL GC 的 STW 阶段,导致整个系统随着数据逐渐增加而服务能力下降,优化后系统稳定运行提供快速检索能力。
三、性能优化-索引篇
优化了内存使用后,系统的稳定性得到了保障。因为我们的es主要用于日志和skywalking 的数据存储检索,其使用方式写入远远大于检索,所以对于其写入能力也有着一定的要求。
当前目前对于skywalking 的写入如下
可以发现其写入能力非常低
在优化前必然要了解其核心概念,优化都是基于理论基础而来的,网上有很多文章这里不详细说了,下面看一张图理解下:
上图展示了一个doc index/write请求过来,es为其建立倒排的过程,而index opt.的优化点就主要集中在该posting list building过程,
- doc write/index request comes
- 根据自定义的routingId字段或者docId选择routing shard
- 为了提高容错,doc双写
- 写入es实例的memory buffer(此时doc未能被search)
- 写入transLog的内存
- es实例在每个refresh interval里将heap里面的docs刷到lucene利用着的系统缓存里(此时doc能够被search)
- transLog根据配置的持久化到disk的策略,同步docs到磁盘(顺序写盘)
- transLog的clean up
先看一下优化参数模版
PUT _template/skywalking
{
"index_patterns": "skywalking*",
"settings": {
"index.indexing.slowlog.threshold.index.debug" : "2s",
"index.indexing.slowlog.threshold.index.info" : "5s",
"index.indexing.slowlog.threshold.index.trace" : "500ms",
"index.indexing.slowlog.threshold.index.warn" : "10s",
"index.optimize_auto_generated_id" : "true",
"index.refresh_interval" : "30s",
"index.search.slowlog.threshold.fetch.debug" : "500ms",
"index.search.slowlog.threshold.fetch.info" : "800ms",
"index.search.slowlog.threshold.fetch.trace" : "200ms",
"index.search.slowlog.threshold.fetch.warn" : "1s",
"index.search.slowlog.threshold.query.debug" : "2s",
"index.search.slowlog.threshold.query.info" : "5s",
"index.search.slowlog.threshold.query.trace" : "500ms",
"index.search.slowlog.threshold.query.warn" : "10s",
"index.translog.durability" : "async",
"index.translog.flush_threshold_size" : "1gb",
"index.translog.sync_interval" : "60s"
}
}
对于网上很多资料有很多优化参数,这里我们并不需要调整每一个,通过理论可以看出,对于写入影响点有以下几个:
memory Buffer -> FilesystemCache 影响参数 refresh_interval: 1s,(将新生成的segment 刷入文件系统缓存中,才可以被搜索到)
transLog-> Disk 每次 index、bulk、delete、update 完成的时候,一定触发刷新 translog 到磁盘上,才给请求返回 200 OK
transLog-> empty transLog 这个阶段主要影响内存的回收,可以根据 大小 条数 时间自由设定,默认30m | 200M flush
auto doc id 如果手动为es doc设置一个id,那么es在每个write req都要去确认那个id是否存在
再经过如上简单的参数优化后,整体索引情况如下
但3k 的index 速度也是远远达不到我们的要求的,这里将使用esrally 对单分区进行下写入压力测试。
四、性能优化-搜索篇(待完成)