知识库

缓解由高GC导致的因果集群重新选举

本文描述了JVM“停止一切”(stop-the-world) 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错误)。此外,增加Java堆空间的大小也往往会增加GC暂停的时间长度。可以执行堆转储,并使用例如Eclipse MAT(确保您的机器有足够的内存进行分析)来诊断内存泄漏。其他工具,如JDK、jconsolevisualvmjstat,以及neo4j gc.log,可能有助于识别任何异常事务。

GC暂停如何影响因果集群?

上述长时间GC或内存泄漏的一个常见后果是集群中的心跳超时,这本质上是集群成员无法及时连接到其他成员。这是因为完整GC强制执行“停止一切”暂停,在此期间,JVM会暂停所有其他操作,包括网络通信。在因果集群中,这通常会导致重新选举,不可达的成员会从集群中移除,并选举出新的集群领导者。请注意,可能导致重新选举的其他因素通常是良性的,并且不在本文讨论范围之内。

重新选举及其在重负载下的频率为何重要?

因果集群中的重新选举是一个快速、无缝的过程,在正常操作下,它发生在几十/几百毫秒内,对事务客户端没有明显影响。重新选举是领导者在暂停期间变得不可用的自然结果;跟随者不知道领导者正在进行垃圾回收,也不应该关心。然而,在高负载情况下,加上频繁的长GC暂停,重新选举可能是不受欢迎的。这是因为在重新选举期间,每单位时间的入站事务(因此被拒绝的事务)数量很高。此外,新当选的领导者可能需要通过检查点将任何挂起的事务提交到存储,并成为追随者赶上的新来源,同时大量新传入的事务将再次需要频繁且可能长时间的GC暂停,对操作系统CPU和内存资源产生累积效应。此外,前任领导者可能在短时间内重新加入集群,其日志中仍有更高的事务ID,因此再次成为领导者。重要的是,虽然选举本身可能持续几毫秒,但在选举期间,集群将不接受写入。这些是可能导致循环持续领导者切换的一些关键因素,尤其是在重负载情况下。

选举和心跳超时。它们是什么?

选举超时是指在FOLLOWER尝试开始选举并成为CANDIDATE之后的时间。这些超时在选举期间持续存在,如果CANDIDATE未当选,或在又一个causal_clustering.leader_election_timeout周期后未收到新当选LEADER的消息,它将要么成为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/

什么是Pre_Voting以及它如何提供帮助?

另一个有助于防止频繁重新选举的配置是causal_clustering.enable_pre_voting(在Neo4j 3.2.9中添加),它控制是否启用“预选举”。Pre_voting是一个实例询问其他集群成员的过程:“如果我发起选举,你会投我的票吗?”。这是一种乐观的方法,用于阻止潜在的无效选举(正如我们所知,无效选举发生时会停止写入)。在这种情况下,“无效”通常意味着前任领导者仍然是领导者,而我们在“无效”选举发生期间所得到的只是几秒钟的不可用性。选举后,“任期”(term)最高的集群成员获胜并成为新领导者。请注意,成员只有在其他成员投票给他们时才会增加他们的任期,并且如果追随者具有更高的任期,领导者必须下台,这意味着领导者落后了。

因此,预投票有助于通过两种方式减少不必要的选举:

  1. 它确保除非法定人数与领导者失去心跳连接,否则不会触发选举。

  2. 它确保在集群中任期低于其他成员的成员不会触发选举。

© . All rights reserved.