减轻由高 GC 引起的因果集群重新选举
本文介绍了 JVM 停止世界 GC 暂停对因果集群的影响。首先简要介绍了垃圾回收、堆大小调整和内存泄漏故障排除,然后讨论了有助于缓解由此产生的心跳超时和集群重新选举的最佳实践和配置。
GC 概览
JVM(Java 虚拟机,例如 Neo4J 服务器)会为新创建的 Java 对象分配所需的内存。此内存是通过 neo4j.conf 在启动时配置的总堆内存分配的一部分。JVM 定期检查堆中未使用的对象,这些对象随后被丢弃以通过称为垃圾回收
或简称GC
的过程回收堆内存。
假设已使用 Neo4j 内存配置(请参阅 https://neo4j.ac.cn/developer/kb/understanding-memory-consumption/)对堆大小进行了最佳调整,但使用/数据量的峰值仍可能导致堆利用率增加,从而导致垃圾回收时间更长和/或更频繁。内存泄漏是堆利用率增加的另一个原因,在许多情况下会导致堆内存不足的情况。Java 中的内存泄漏是指某些对象不再被应用程序使用,但垃圾回收无法识别这一点。
在这种情况下,添加更多堆内存可能只会推迟 JVM 用尽堆空间(抛出java.lang.OutOfMemoryError: Java heap space error
)。此外,增加 Java 堆空间的量也往往会增加 GC 暂停的持续时间。您可以执行堆转储并使用例如 Eclipse MAT(确保您的机器有足够的内存进行分析)来诊断内存泄漏。其他工具如 JDK、jconsole
、visualvm
、jstat
以及 neo4j gc.log 确实可以帮助识别任何异常事务。
GC 暂停如何影响因果集群?
上述长时间 GC 或内存泄漏的一个常见后果是集群中的心跳超时,这本质上是集群成员无法及时到达其他成员。这是因为完整的 GC 会强制执行停止世界暂停,在此期间,JVM 会停止所有其他操作,包括网络通信。在因果集群中,这通常会导致重新选举,即将无法访问的成员从集群中移除并选举新的集群领导者。请注意,还有其他因素可能导致重新选举,但它们通常是良性的,不在本文讨论范围内。
重新选举以及在高负载情况下其频率为何重要?
因果集群中的重新选举是一个快速、无缝的过程,在正常操作下,在几毫秒到几百毫秒内发生,对事务客户端没有任何明显的影响。重新选举是领导者在暂停期间实际上变得不可用这一事实的自然结果,跟随者不知道领导者正在进行垃圾回收,也不应该关心。但是在高工作负载的情况下,加上频繁的高 GC 暂停,重新选举可能是不可取的。这是因为在重新选举期间,单位时间内传入的事务数量以及因此被拒绝的事务数量很高。此外,新当选的领导者可能需要通过检查点将任何挂起的事务提交到存储,并成为跟随者赶上的新来源,而大量新传入的事务将再次需要频繁且可能长时间的 GC 暂停,对操作系统 CPU 和内存资源产生累积影响。此外,之前的领导者可能会在很短的时间内重新加入集群,其日志中仍然拥有更高的事务 ID,因此再次成为领导者。重要的是,虽然选举本身可能持续几毫秒,但在选举期间,集群将不接受写入。这些是一些可能导致循环连续领导者切换的关键因素,尤其是在高工作负载的情况下。
选举和心跳超时。它们是什么?
选举超时是指FOLLOWER
尝试开始选举并成为CANDIDATE
的时间。这些超时在选举期间继续,并且如果CANDIDATE
未能当选,或者在另一个causal_clustering.leader_election_timeout
周期后未能从新当选的LEADER
处听到消息,则CANDIDATE
将成为FOLLOWER
或开始另一次选举。一般来说,较长的选举超时是不可取的。它们代表了领导者故障转移期间更长的停机时间。目标不应该是增加它,而是管理工作负载以减少 GC。对于第二次超时设置稍长的时间段,可能会减少选举引起的整体停机时间。causal_clustering.leader_election_timeout
的已记录定义没有提及心跳超时,因为心跳是实现细节。大多数集群内消息也被视为心跳。如果集群成员从领导者那里收到消息,则认为它已从该领导者那里听到消息,因此选举超时会重置超时,之后将触发新的领导者选举。
如何最好地缓解长时间 GC 后的心跳/选举超时?
因果集群心跳超时通过配置causal_clustering.leader_election_timeout
控制,默认为 7 秒,推荐值小于 10 秒。对于具有大型堆的图,GC 暂停有时可能长达几分钟,在这种情况下,此类超时值将无法防止重新选举。请注意,并非大型堆本身会导致 GC,而是分配远远超过 GC 能够收集的内存,最终导致停止世界
暂停。如果存在许多较小的暂停,这些暂停偶尔会导致领导者选举(因此只是偶尔错过 7 秒的超时),那么通过一个小值增加causal_clustering.leader_election_timeout
可能是一种解决方案,但唯一的真正解决方案是生成更少的垃圾,这通常涉及重写 Cypher 查询并避免大型数据摄取查询等。查询优化超出了本文的范围,但它是 Neo4J Cypher 手册的一部分,可在以下位置获得: https://neo4j.ac.cn/docs/cypher-manual/current/query-tuning/。
什么是预投票以及它如何提供帮助?
另一个有助于防止频繁重新选举的配置是causal_clustering.enable_pre_voting
(在 Neo4j 3.2.9 中添加),它控制是否启用“预选举”。预投票是指实例询问其他集群成员:“如果我发起选举,您会投票给我吗?”。这是一种乐观的方法,可以停止可能毫无意义的选举(我们知道它们会在选举时停止写入)。在这种情况下,“毫无意义”通常意味着之前的领导者仍然是领导者,我们得到的只是选举期间几秒钟的不可用时间,而“毫无意义”的选举正在发生。在选举之后,具有最高“任期”的集群成员获胜并成为新的领导者。请注意,成员只有在其他成员投票给他们时才会增加他们的任期,并且如果跟随者具有更高的任期,则领导者必须辞职,这意味着领导者落后了。
因此,预投票通过两种方式帮助减少不必要的选举
-
它确保只有在法定人数与领导者失去心跳时才会触发选举。
-
它确保集群中任期低于其他成员的成员不会触发选举。
此页面是否有帮助?