概要
本篇继续讲解Elasticsearch集群部署的细节问题
集群重启问题
如果我们的Elasticsearch集群做了一些离线的维护操作时,如扩容磁盘,升级版本等,需要对集群进行启动,节点数较多时,从第一个节点开始启动,到最后一个节点启动完成,耗时可能较长,有时候还可能出现某几个节点因故障无法启动,排查问题、修复故障后才能加入到集群中,此时集群会干什么呢?
假设10个节点的集群,每个节点有1个shard,升级后重启节点,结果有3台节点因故障未能启动,需要耗费时间排查故障,如下图所示:
整个过程步骤如下:
-
集群已完成master选举(node6),master发现未加入集群的node1、node2、node3包含的shard丢失,便立即发出shard恢复的指令。
-
在线的7台node,将其中一个replica shard升级为primary shard,并且进行为这些primary shard复制足够的replica shard。
-
执行shard rebalance操作。
-
故障的3台节点已排除,启动成功后加入集群。
-
这3台节点发现自己的shard已经在集群中的其他节点上了,便删除本地的shard数据。
-
master发现新的3台node没有shard数据,重新执行一次shard rebalance操作。
这个过程可以发现,多做了四次IO操作,shard复制,shard首次移动,shard本地删除,shard再次移动,这样凭空造成大量的IO压力,如果数据量是TB级别的,那费时费力不讨好。
出现此类问题的原因是节点启动的间隔时间不能确定,并且节点越多,这个问题越容易出现,如果可以设置集群等待多少个节点启动后,再决定是否对shard进行移动,这样IO压力就能小很多。
针对这个问题,我们有下面几个参数:
- gateway.recover_after_nodes:集群必须要有多少个节点时,才开始做shard恢复操作。
- gateway.expected_nodes: 集群应该有多少个节点
- gateway.recover_after_time: 集群启动后等待的shard恢复时间
如上面的案例,我们可以这样设置:
gateway.recover_after_nodes: 8
gateway.expected_nodes: 10
gateway.recover_after_time: 5m
这三个参数的含义:集群总共有10个节点,必须要有8个节点加入集群时,才允许执行shard恢复操作,如果10个节点未全部启动成功,最长的等待时间为5分钟。
这几个参数的值可以根据实际的集群规模来设置,并且只能在elasticsearch.yml
文件里设置,没有动态修改的入口。
上面的参数设置合理的情况,集群启动是没有shard移动的现象,这样集群启动的时候就可以由之前的几小时,变成几秒钟。
JVM和Thread Pool设置
一提到JVM的调优,大家都有手痒的感觉,好几百个JVM参数,说不定开启了正确的按钮,从此ES踏上高性能、高吞吐量的道路。现实情况可能是我们想多了,ES的大部分参数是经过反复论证的,基本上不用咱们太操心。
JVM GC
Elasticsearch默认使用的垃圾回收器是CMS。
## GC configuration
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
CMS回收器是并发式的回收器,能够跟应用程序工作线程并发工作,最大程度减少垃圾回收时的服务停顿时间。
CMS还是会有两个停顿阶段,同时在回收特别大的heap时也会有一些问题。尽管有一些缺点,但是CMS对于要求低延时请求响应的软件来说,还是最佳的垃圾回收器,因此官方的推荐就是使用CMS垃圾回收器。
有一种最新的垃圾回收器叫做G1。G1回收器可以比CMS提供更少的回收停顿时间,而且能够这对大heap有更好的回收表现。它会将heap划分为多个region,然后自动预测哪个region会有最多可以回收的空间。通过回收那些region,就可以最小化停顿时长,而且可以针对大heap进行回收。
听起来还挺好的,只是G1还是比较年轻的一种垃圾回收器,而且经常会发现一些新的bug,这些bug可能会导致jvm挂掉。稳定起见,暂时不用G1,等G1成熟后ES官方推荐后再用不迟。
线程池
我们开发Java应用系统时,对系统调优的一个常见手段就是调整线程池,但在ES中,默认的threadpool设置是非常合理的,对于所有的threadpool来说,除了搜索的线程池,都是线程数量设置的跟cpu core一样多的。如果我们有8个cpu core,那么就可以并行运行8个线程。那么对于大部分的线程池来说,分配8个线程就是最合理的数量。
搜索会有一个更加大的threadpool,线程数量一般被配置为:cpu core * 3 / 2 + 1。
Elasticsearch的线程池分成两种:接受请求的线程和处理磁盘IO操作的线程,前面那种由ES管理,后一种由Lucene管理,它们之间会进行协作,ES的线程不会因为IO操作而block住,所以ES的线程设置跟CPU核数一样或略大于CPU核数即可。
服务器的计算能力是非常有限的,线程池的数量过大会导致上下文频繁切换,更费资源,如果threadpool大小设置为50,100,甚至500,会导致CPU资源利用率很低,性能反而下降。
只需要记住:用默认的线程池,如果真要修改,以CPU核数为准。
heap内存设置最佳实践
Elasticsearch默认的jvm heap内存大小是2G,如果是研发环境,我会改成512MB,但在生产环境2GB有点少。
在config/jvm.options文件下,可以看到heap的设置:
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space
-Xms2g
-Xmx2g