elasticsearch 一次长时间 FGC 问题分析

elasticsearch 一次长时间 FGC 问题分析


事件起因:

es 版本:5.5.3
集群节点数:3,默认配置
使用 java rest api进行写入压力测试时,给写入程序设置较短的超时时间,让写入请求产生大量超时,然后立刻发起下一个 bulk 写入请求,es协调节点很快会长时间的 FGC 卡住.暂停处理所有任务.

事件结论


1.netty 内存池没有释放,客户端的 bulk 请求累积的内存池,即使 bulk 队列不超,一段时间后也会 长时间 FGC
2.协调节点bulk 队列会超过设定大小非常多,设置200,实际可能到1000+

问题定位


bulk 队列暴增问题

既然是 FGC 频繁且持续久,必然是大量临时对象没有来得及释放,通过 _cat/thread_poll 接口发现 bulk 队列,会持续增加,严重超过设置值.阅读相关实现代码,当 bulk 请求为replica时会 forcePut 到队列,不检查大小,类似的除了 bulk 之外还有 search 等十余个 action

回想一下协调节点对 bulk 的处理过程:接收客户端 bulk 请求的 http_server_worker 线程对 bulk 请求进行合并,组装成基于 shard 的 bulk 请求,放到 bulk 队列,然后由 bulk 线程本地执行,或者转发到该 shard 所在主分片.

在此流程下,所有节点的 bulk 队列即使超过设置值,每个节点的 bulk 队列大小都应该是相似的,不应该只有协调节点队列暴涨,除非数据分布不均,或者节点处理速度不同。对于第一个问题,_cat/shard 接口查看分布是均匀的,第二个问题,将客户端请求发送到其他节点,总是接受客户端请求协调节点bulk队列暴涨,证明与物理主机无关,单机模拟两节点集群,一个运行与控制台,一个运行于 IDE,IDE 中运行的节点在相同验证中会产生堆积

协调节点处理慢于其他节点的主要原因在于 netty 的内存池堆积引起的 GC 慢,即使协调节点不存在主分片,bulk 队列不超,jstat 观察程序 GC 时间明显长与其他节点。协调节点的协调工作应该不足以明显影响索引速度

netty 内存池累积问题

既然 bulk 队列会超,让协调节点不存储主分片,只有副本,再次测试,发现队列不超,但一段时间后仍被长时间 FGC 卡住,但情况有所缓解,出现问题的时间点比之前晚。这个时候需要知道内存里到底是什么东西,

执行

在即将 FGC 停住前 dump 内侧,MAT 分析占用资源最多的对象:

发现是 netty 的内存池资源,阅读 es 中相关申请和释放的代码,申请内存池的时机为:在接收到客户端 bulk 请求时由 netty 层负责,释放的时机呢?通过加断点和延迟释放发现,需要协调节点给客户端回复 Response 时才释放,由 es 控制。在此之前即使客户端断开连接也不释放

综合结论

  1. 写入客户端发送成功后即使关闭 TCP 连接,es 也会把收到的 bulk 请求正常处理。
  2. bulk 队列 forcePut 的条件为写副本请求: bulk[s][r] 。如果协调节点中只有主分片,bulk 队列不会超过设置值
  3. 协调节点接收客户端 bulk 写入请求,合并为基于 shard 的请求是在http_server_worker立即处理的,而非 bulk 线程
  4. 在接收处理 bulk 时,netty 内存池的申请时机为收到客户端请求,释放时机为协调节点恢复客户端 Response,由 es 控制

解决方案


默认配置下的 es 写入速度由于 translog 刷盘时机等因素,写入速度较慢,压力测试下容易出现这种问题,参考《将 elasticsearch 写入速度优化到极限》调整刷盘频率等之后则很难出现,但请仍然参考下列建议:

  • bulk 队列够用就行,不要过大,他们占用的内存会成为 GC 的负担
  • 同理,单个 bulk 请求体的数据量不要太大,官方建议大约5-15mb
  • 写入端的 bulk 请求超时需要足够长,建议60s 以上
  • 写入端尽量将数据轮询打到不同节点。

按照系统化的调试思想,定位问题的过程就是逐步缩小问题范围。验证一个想法可能有多种方式,一定要选能证明问题,同时最简单的方式。

(转载请注明作者和出处 easyice.cn ,请勿用于任何商业用途)

1 Star2 Stars3 Stars4 Stars5 Stars (4 人打了分, 平均分: 4.75 )
Loading...

发表评论

邮箱地址不会被公开。 必填项已用*标注