es堆内存配置
- -Xms30g -Xmx30g
- -XX:CMSInitiatingOccupancyFraction=75
- 查看具体的堆内存的分配大小
/usr/local/java8/bin/jstat -gccapacity 17394 1000
- survivor: 629120.0,629M
- eden: 5033216.0, 5G
- old: 25165824.0, 25G
cms正常的并发gc
CMS通过将大量工作分散到并发处理阶段来在减少STW时间,在这块做得非常优秀。
几个关键的时间日志:
- 0.138/0.138 secs:这个阶段的持续时间与时钟时间;
- user:GC线程在垃圾收集期间所使用的CPU总时间;
- sys:系统调用或者等待系统事件花费的时间;
- real:应用被暂停的时钟时间,由于GC线程是多线程的,导致了real小于(user+real),如果是gc线程是单线程的话,real是接近于(user+real)时间。
下面个是一段ParNew和CMS结合的垃圾回收日志:
1 | # 年轻代gc之后,对象分配失败 |
上面的垃圾回收日志是正常的一个回收过程,年轻代放不下之后,会将对象升级到老年代,老年代到达cms设置的阈值之后,会触发老年代的回收,上述回收过程正是cms的优势所在,一次gc过程,大部分时间是跟应用线程并发执行,对应用线程的影响比较小,对于一般的应用基本上也是感知不到的。
但是有一种情况下的gc会对应用造成很大的影响,cms本身有一个比较大的缺陷在于,无法处理浮动垃圾,单纯的标记清除算法,随着垃圾回收的进行,老年代的碎片也会越来越多,一次old gc的时候,如果新一波的young gc之后升级到老年代的对象,在目前老年代中仍然放不下,会产生concurrent mode failure,此时会造成单线程的fullgc,应用线程会被暂停,如果堆内存比较大,就像我们的es机器,堆内存30G,一次fullgc需要花费20多秒的时间,对线上的应用的影响会非常大。
concurrent mode failure
CMS 收集器无法处理浮动垃圾(Floating Garbage),可能出现 “Concurrnet Mode Failure” 失败而导致另一次Full GC的产生,可能引发串行Full GC;下面是一段产生该异常的gc日志,gc造成es正常的线程停顿了26s的时间:
1 | 2021-02-23T07:01:47.432+0800: 199651.034: [GC (CMS Initial Mark) [1 CMS-initial-mark: 19184059K(25165824K)] 19829584K(30828160K), 0.0135926 secs] [Times: user=0.10 sys=0.00, real=0.01 secs] |
gc日志的设置
这个gc日志不是es特有的设置,而是jvm本身的设置
1 | -Xloggc:/opt/meituan/es_logs/elasticsearch.gc.log |
之前遇到fullgc的问题,为了避免对线上应用的影响,第一时间重启了es进程,由于es进程配置的gc日志只保存在一个文件中,重启之后日志被重写,我们无法去分析当时的一个gc日志情况;这里开启了日志滚动的开关,生成的日志的形式如下:
1 | elasticsearch.gc.log |
- 上面表示,当前正在写的文件的编号是2,假如文件的个数是3个,那么编号2的写完了之后,会继续写后缀为0的文件,就是这种滚动写日志文件的形式;
- 每次程序启动的时候,都会从编号0开始,重新创建后缀0的文件
- 假设目前程序生成了文件0、1、2,重启之后,会重新写文件0,因此文件2中的内容还保存,也就是重启之前的gc的内容还可以去查看,分析重启之前的gc现场
- 存在一个问题,假设日志文件比较少,日志还没有滚动,只有文件0,这时候,重启了程序,文件0会重新创建,这时候之前的日志内容就没了
es线上稳定性的思考
事前、事中、事后
- 事前:监控、慢查询梳理
- 事中:踢除有问题节点,保证线上查询正常;重启节点,需要注意的是保存好事故现场
- 直接杀掉有问题的节点,缺点无法保留问题现场
- 事后:分析问题出现的原因,解决问题
- 优化索引结构,优化慢查询
- 开发小工具,保证问题出现的时候,线上服务影响程度最小化
当然这不是一个一次性的工作,需要针对问题不断去优化事前、事中、事后的处理逻辑
- 比如说:监控是依赖es node api的,node api需要获取每个节点的状态,当一个节点full gc之后,整个api不返回结果了,监控无效了
- 通过线程池api,提前发现问题
- 获取节点的工具化页面,在full gc之后也无法获取到,通过cellar缓存解决
- gc log有问题,只保存在一个文件中,每次重启文件被重写,无法分析当时问题现场的gc日志