公司最近使用ElasticSearch作为数据报表汇总引擎.上线三个月累计数据800万,但是今天突然大面积出现查询超时,上服务器查看服务运行情况,发现cpu使用率高达300% mem 使用率也到了90%,下面记录了整个排查问题的过程

     1.首先查看elastic cpu和mem占用情况

//首先查看所有进程
# top
//找到对应的elasticserch运行的PID,查看占用内存比较高的线程,Ctrl+c 退出
#top -Hp 12345   
//为了下面方便调用API,将elasticsearch.yml
// xpack.security.enabled: false
//关闭认证,重启elasticsearch

    2.确定是由于某个线程引起的状况,查询所有segment的驻留内存的情况

//kibana 以下全部以kibanna为例
GET  _cat/segments?v&h=index,segment,size
//curl
#curl -XGET 'http://10.0.7.134:9200/ _cat/segments?v&h=index,segment,size'

    这里摘录一下,elasticsearch,segment,shard的区别

Shard(分片) 
      一个Shard就是一个Lucene实例,是一个完整的搜索引擎。一个索引可以只包含一个Shard,只是一般情况下会用多个分片,可以拆分索引到不同的节点上,分担索引压力。

segment 
     elasticsearch中的每个分片包含多个segment,每一个segment都是一个倒排索引;在查询的时,会把所有的segment查询结果汇总归并后最为最终的分片查询结果返回; 
     在创建索引的时候,elasticsearch会把文档信息写到内存bugffer中(为了安全,也一起写到translog),定时(可配置)把数据写到segment缓存小文件中,然后刷新查询,使刚写入的segment可查。 
虽然写入的segment可查询,但是还没有持久化到磁盘上。因此,还是会存在丢失的可能性的。 
      所以,elasticsearch会执行flush操作,把segment持久化到磁盘上并清除translog的数据(因为这个时候,数据已经写到磁盘上,不在需要了)。 
当索引数据不断增长时,对应的segment也会不断的增多,查询性能可能就会下降。因此,Elasticsearch会触发segment合并的线程,把很多小的segment合并成更大的segment,然后删除小的segment。 
     segment是不可变的,当我们更新一个文档时,会把老的数据打上已删除的标记,然后写一条新的文档。在执行flush操作的时候,才会把已删除的记录物理删除掉。

   3.没有发现特别占用内存和cpu的segment,接着查看node的内存和cpu的使用情况

GET _cat/nodes?v

   参考: Day 19 ES内存那点事

 4.经过查找整体的cpu占用率还是很高,接着查看,elastic运行日志发现

//jvm gc
[gc][51125] overhead, spent [15s] collecting in the last [15.2s]

     这里摘录一下java jvm gc的解释

与C/C++相比,JAVA并不要求我们去人为编写代码进行内存回收和垃圾清理。JAVA提供了垃圾回收器(garbage collector)来自动检测对象的作用域),可自动把不再被使用的存储空间释放掉,也就是说,GC机制可以有效地防止内存泄露以及内存溢出。

JAVA 垃圾回收器的主要任务是:

分配内存
确保被引用对象的内存不被错误地回收
回收不再被引用的对象的内存空间
凡事都有两面性。垃圾回收器在把程序员从释放内存的复杂工作中解放出来的同时,为了实现垃圾回收,garbage collector必须跟踪内存的使用情况,释放没用的对象,在完成内存的释放之后还需要处理堆中的碎片, 这样做必定会增加JVM的负担。

为什么要了解JAVA的GC机制? 综上所述,除了作为一个程序员,精益求精是基本要求之外,深入了解GC机制让我们的代码更有效率,尤其是在构建大型程序时,GC直接影响着内存优化和运行速度。

     关于java程序的性能   G1 Garbage Collector *

    [ElasticSearch填坑] 聚合请求导致GC故障  *

      生产环境elasticsearch的配置建议

      jvm如何优化java GC[译]

   5.查看节点状态

//kibana
_nodes/stats
//jvm 占用cpu 98% 内存 80%
需要提的一点就是 GC 是非常影响性能的,所以我们来简单介绍一下 JVM 的机制。启动 JVM 虚拟机的时候,会分配固定大小的内存块,也就是堆 heap。堆又分成两组,Young 组是为新实例化的对象所分配的空间,比较小,一般来说几百 MB,Young 组内又分为两个 survivor 空间。Young 空间满了后,就垃圾回收一次,还存活的对象放到幸存空间中,失效的就被移除。Old 组就是保存那些重启存活且一段时间不会变化的内容,对于 ES 来说可能有 30 GB 内存是 Old 组,同样,满了之后就垃圾回收。

垃圾回收的时候,JVM 采用的是 STW(Stop The World) 机制,Young 组比较小还好,但是 Old 组可能需要几秒十几秒,那就是服务器无响应啊!所以我们必须非常关注 GC 性能。

如果 ES 集群中经常有很耗时的 GC,说明内存不足,如果影响集群之间 ping 的话,就会退出集群,然后因为分片缘故导致更大的影响。我们可以在节点状态中的 jvm 部分查看对应的数值,最重要是 heap_used_percent,如果大于 75,那么就要垃圾回收了,如果长期在 75 以上,那就是内存不足。

Elasticsearch技巧指南 **

  6.综合上述,可以定位

     6.1  jvm heap 设置比较小,默认是2g 本机有10g 配置内存为6g

     6.2 jvm GC 配置原来为 UseConcMarkSweepGC 更新为 UseG1GC

    修改elasticSeaerch安装目录下,jvm.options ,如果是集群则每个节点都要改

//注释掉原来的配置
## GC configuration
#-XX:+UseConcMarkSweepGC
#-XX:CMSInitiatingOccupancyFraction=75
#-XX:+UseCMSInitiatingOccupancyOnly

//更新为
-XX:+UseG1GC
-XX:MaxGCPauseMillis=50

//修改

-Xms6g
-Xms6g

    改elasticsearch.yml打开认证,重新启动ElasticSearch,在查看cpu没有那么高了,而且查询速度也快了

    貌似内存消耗还是有点高,但是正常情况,通过使用

    _cat/segments?v查看是具体情况