Elasticsearch JVM详解

Elasticsearch JVM 配置概览

JVM参数 Elasticsearch默认值 Environment变量
-Xms 256m ES_MIN_MEM
-Xmx 1g ES_MAX_MEM
-Xms and -Xmx ES_HEAP_SIZE
-Xmn ES_HEAP_NEWSIZE
-XX:MaxDirectMemorySize ES_DIRECT_SIZE
-Xss 256k
-XX:UseParNewGC +
-XX:UseConcMarkSweepGC +
-XX:CMSInitiatingOccupancyFraction 75
-XX:UseCMSInitiatingOccupancyOnly +
-XX:UseCondCardMark (commented out)

我们可以注意到ES JVM Heap内存设置为在256M在1GB之间.这个设置是为在开发和示范环境中使用的,开发人员可以通过简单地安装ES就可以使用了,但是这样的内存设置在很多情况下都是不够用的,我在需要设置更大的值。

image description

ES_MIN_MEM/ES_MAX_MEM用于控制jvm的堆内存,另外还有ES_HEAP_SEIZ,这样我可以设置更多的堆内存用于ES,另外建议不在启动内存堆平衡,因为这样会浪费很大的性能。

ES_HEAP_NEWSIZE这个参数用于控制堆内存的子集,即新生代堆控制。

ES_DIRECT_SIZE,我们可以对应到Direct Memory Size这个参数,在JVM管理数据中使用的是NIO,本机内存可以映射到虚拟地址空间,在X64的架构上更有效,在ES中没有选择进行设置,但是有一个问题,本机直接内存的分配不会受到Java堆大小的限制,但是即然是内存那肯定还是要受到本机物理内存(包括SWAP区或者Windows虚拟内存)的限制的,一般服务器管理员配置JVM参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),而导致动态扩展时出现OutOfMemoryError异常。

下面例出一些JVM参数设置

JVM parameter Garbage collector
-XX:+UseSerialGC serial collector
-XX:+UseParallelGC parallel collector
-XX:+UseParallelOldGC Parallel compacting collector
-XX:+UseConcMarkSweepGC Concurrent-Mark-Sweep ( CMS ) collector
-XX:+UseG1GC Garbage-First collector (G1)
  • UseParNewGC和UseConcMarkSweepGC是结并并行和行发性的垃圾回收机制,在JAVA6中将默认为UserParNewGC和UseGoncMarkSweepGC并禁用串行收集器。
  • CMSInitiatingOccupancyFraction垃圾回收,这个75是指,到heap占用到75%时开发进行清理,我们知道堆分为新生代和老年代两块可新生代一块为老年代的两倍,也许在没有达到75%时也可能进行垃圾回收。
  • UseCondCardMark 将在在高度并发的情况下,将些值注释掉

总结:

1、修改MAX 和MIN Heap大小设置。
2、设置垃圾回收百分比
3、如果在JAVA7中禁用默认的G1垃圾回收机制。

JVM进程的内存结构

image description

JVM内存分为如下几段:

  • JVM CODE用于内部代码存放
  • Noe-heap memory用于加载类
  • Stack memory 用于存放本地变量和线程操作数
  • Heap memory 存放引用类型对象
  • Direct Buffer,缓冲输入,输出数据

Heap memory大小设置是非常重要的,因为java的运行取决于一个合理的heap的大小,如果设置太小,在许多垃圾回收或是高性能的情况下就会出现OutOfMemory异常。如果堆太大,垃圾回收将需要更大的数据,该算法将要面对更高数量的存活堆,这样操作系统也会面对较大的压力。

Non-heap内存分配是由java应用程序自动设置的,没有办法控制这个参数,因为它是由JAVA应用程序代码决定的。

垃圾回收与Lucene段

在ES中的垃圾回收器是集用的CMS垃圾回收,这种回收器不是提高敢回收的效率可是降低了回收的次数,但是面对比较大的数据集合时,这种回收可能需要的时间更长。 而这种大的数据集合主要是在Lucene的索引中,因些可以将索引的段进行一行调优工作,提高GC的效率。 index.merge.policy.segments_per_tier

减少分页

在大堆内存的情况下,如果内存不足时会与操作系统的SWAP空间进行分页数据的交换,但是这种交换是非常慢的,这种会降低整体性能。

垃圾回收器的选择

JAVA 7中的默认是G1垃圾回收器,这种回收器和CMS回收相对,他在于处理吞吐量,但是如果在大堆的情况下CMS回收器在性能上将超过G1.

性能调优策略

1、 收集日志

2、 对日志进行分析

3、 选择你要优化的目标

4、 计划优化

5、 应用新有设置

6、 监控程序在新设置后的运行情况

7、 反复试尝

ES 垃圾回收日志分析

[es-date-1224] [gc][young][3402090][244044] duration [887ms],collections [1]/[1.5s], total [887ms]/[3.3h], memory [4.5gb]->[4gb]/[6.9gb],all_pools {[young] [499.4mb]->[782.8kb]/[532.5mb]}{[survivor][32.7mb]->[30.2mb]/[66.5mb]}{[old] [3.9gb]->[3.9gb]/[6.3gb]}

上面这个例子的情况无须紧张,只是young gc,并且只用了887ms,对于Elasticsearch而言,没有啥影响。唯一需要留心的是,如果在日志中出现连续的和长时间的young gc,则需要引起警觉,可能是你的Heap内存分配不够。

[es-data-1224] [gc][old][76581][22] duration [3.1m], collections[2]/[3.1m], total [3.1m]/[3.1m], memory [3gb]->[1.2gb]/[3.4gb], all_pools{[young] [251mb]->[74.9mb]/[266.2mb]}{[survivor][25.8mb]->[0b]/[33.2mb]}{[old] [2.8gb]->[1.1gb]/[3.1gb]}

如果这种JVM出现,则你的节点一定被踢出了集群。old gc是比较耗时,上面这个例子用了3.1分钟,一定是出了啥大事,要不是然“世界”不会停转这么久的,呵呵!

建议

1、ES不要运行在6U22之前因之多版本的JDK存在许多的bug,尽量使用Sun/Oracle比较最出的JDK6-7因为里面修复很多bug.如果在JAVA7正式发布的情况下最好使用JDK7(不过要到2013了)

2、考虑到ES是一个比较新的软件,利用最先的技术来获取性能,尽量从JVM中来挤压性能,另外检索您的操作系统是否是最新版的,尽量使用最新版的操作系统。

3、做好随时更新JAVA版本和ES的版本的情况,因为每季度或是每年都会有新的版本出来。所以在做好版本更新的准备

4、测试从小到大,因为ES的强在多个节点的部署,一个节点是不足以测试出其性能,一个生产系统至少在三个节点以上。

5、测试JVM

6、如果索引有更新请记住对索引段的操作(index.merge.policy.segments_per_tier)

7、在性能调优之前,请先确定系统的最大性能和最大吞吐量

8、启用日志记录对JAVA垃圾回怍机制,有助于更好的诊断,以至于来调整你的系统

9、提高CMS垃圾收集器,您可以添加一个合理的- xx:CMSWaitDuration参数

10、如果堆大小趣过6-8GB,请选择使用CMS