主要观点总结
本文描述了一个针对高并发、低延迟、高内存压力的系统不稳定问题,通过JVM调参优化,最终实现索引无感切换的过程。包括一系列优化尝试,如调整JVM参数、使用不同垃圾收集器等,并详细解释了优化过程中的关键步骤和思路。
关键观点总结
关键观点1: 问题定位与背景介绍
系统因索引切换时GC(垃圾回收)暂停时间长导致服务抖动,影响系统稳定性。通过对系统日志和GC日志的分析,定位到问题的根源。
关键观点2: JVM参数优化
通过调整JVM参数,如MaxTenuringThreshold、PretenureSizeThreshold等,优化索引在JVM中的分配和复制过程,减少GC暂停时间。
关键观点3: 垃圾收集器选择
尝试使用不同的垃圾收集器,如G1GC、ZGC等,以提高垃圾回收效率和降低暂停时间。
关键观点4: 分批断流与Eden区预热策略
通过分批断流和Eden区预热策略,确保索引在切换过程中被快速复制到老年代,消除索引复制时YGC长暂停的负面影响,实现索引无感切换。
关键观点5: 总结与展望
总结整个优化过程的有效手段,并展望未来的改进方向,如更好的优化策略、拓展阅读等。
正文
▐
常
规思路
针对 GC 暂停久这类常见问题,有如下一些常规优化思路:
然而,以上方法在本次场景中大多不适用。首先经排查代码并不存在 Bug,且索引体积已无更多压缩空间,且索引算法层面并不支持增量式更新只能全量更替。其次,加机器虽然能通过稀释单机请求量,让 STW 长暂停影响到的请求量更少,但并未从根本解决问题,且会导致机器资源大量浪费。另外使用堆外内存虽然可不受 GC 管理,但高频访问下序列化/反序列化开销无法容忍。
因此,综合来看只能考虑在 JVM 参数方面做优化:通过修改参数调整 JVM 的行为模式,让索引复制带来的负面影响尽可能小,保障服务高可用。
根据 3.2 节分析,问题已归因为 YGC Object Copy 阶段复制索引时耗时太久,导致上游请求超时报错。本节进一步详细分析 GC 日志,更细粒度还原整个 GC 过程,探索有无潜在优化点。
已知当前 JVM 核心参数如下:
-Xms12g
-Xmx12g
-XX:MetaspaceSize=512m
-XX:MaxMetaspaceSize=512m
-XX:+UseG1GC
-XX:G1HeapRegionSize=16M
-XX:MaxGCPauseMillis=100
-XX:InitiatingHeapOccupancyPercent=45
-XX:+HeapDumpOnOutOfMemoryError
-XX:MaxDirectMemorySize=1g
通过集团
ATP
工具对原始 GC 日志进行可视化分析,下图中标出了各 GC 事件的时间点和变化曲线:
从图中可以分析出如下信息:
① 蓝色圆点:一个点代表一次 YGC,横轴上堆满了密密麻麻的蓝点,说明 YGC 发生非常频繁且耗时短,毫秒级即可完成清理,这是理想中的情况。符合预期
② 粉色折线:代表堆内存占用量的变化情况,可以看到整体呈锯齿形,不断快速上升和下降。由于系统流量较大且请求执行过程会不断产生一些朝生夕灭的临时对象,因此可看到粉色折线快速上升。当 Eden 区不足时触发 YGC 清理,内存释放完成即可看到粉色折线下降到低点。符合预期
③ 异常蓝点:远离横轴说明耗时久,它们就是刚刚在日志中手动找到的长耗时 YGC 记录。
需重点关注
④ 紫色折线:代表老年代堆内存占用量的变化情况。相比之下老年代占用率上涨缓慢,因为大多数临时对象都在年轻代被清理掉了,不会进入老年代。然而观察发现每次长耗时 YGC 蓝点附近,都会伴随着紫色折线阶梯式上升。
需重点关注
其次,还可发现长耗时 YGC 往往是成对出现的,有如下规律:成对出现、时间接近、耗时都长、第一次晋升量少、第二次晋升量多,如下图所示:
综上,整合目前所有已知线索:系统在每次切换索引时,都会超时抖动,且在抖动时间点会发现连续的两次长耗时 YGC(第二次 YGC 晋升量大)。
经分析以上现象符合预期,详细过程推演还原如下:
-
阶段一:系统创建新索引,相关对象默认被分配至 Eden 区;
-
阶段二:Eden 区空间不足,触发
第一次
YGC,此时新索引被
复制
(Object Copy)到 Survivor 区,
耗时久
;
-
阶段三:新索引构造完成,并被 GcRoot 引用上,旧索引与 GcRoot 引用被断开;
-
阶段四:系统持续处理外部请求,Eden 区空间再次不足,触发
第二次
YGC,此时旧索引被清理。新索引又被
复制
(Object Copy)到 Old 区(
晋升
),
耗时久;
-
阶段五:后续即使外部流量再次把 Eden 区打满,YGC 也能毫秒级快速完成。因为只需快速清理临时对象即可,新索引已稳定在老年代不会再被腾挪复制;
至此,问题原因已非常清晰:每次新生成的索引会随着 YGC 连续复制多次,复制过程暂停久导致系统抖动。因此可考虑基于如下一些思路来针对性优化本问题,后文会逐个详细解释: