简介:Neo4j 集群架构

概述

Neo4j 集群提供以下主要功能:

  1. 可扩展性:Neo4j 集群是一组运行多个数据库的服务器集合。服务器和数据库是解耦的:服务器为数据库提供计算和存储资源。每个数据库都有其独立的拓扑结构,分为主库(Primary)和从库(Secondary,用于读取扩展)。

  2. 容错性:主数据库分配提供了一个容错平台以进行事务处理。只要其主分配的大多数成员正常运行,数据库就能保持写入可用状态。

  3. 可操作性:数据库管理与服务器管理是分离的。有关详细信息,请参阅管理集群中的数据库管理集群中的服务器

  4. 因果一致性:当调用时,客户端应用程序可以保证至少读到其自身的写入内容。

有关集群设计模式和反模式的信息,请参阅设计具有弹性的多数据中心集群

运维视图

从运维角度来看,将集群视为运行多个数据库的同构服务器池是有益的。

请注意,主库 (primary)从库 (secondary) 是数据库副本的角色。服务器本身没有角色。相反,它们可以通过设置为 PRIMARYSECONDARYNONEmodeConstraint(模式约束)进行限制。这意味着它们可以托管具有主角色或从角色的标准数据库副本(参见图 1 中的服务器 1 或 3)。如果 modeConstraint 设置为 NONE,则服务器可以托管任何角色的数据库副本。换句话说,一台服务器可以同时托管某些数据库的主库和其他数据库的从库(参见图 1 中的服务器 6)。

同样,即使服务器是集群的一部分,数据库也可能仅托管在一台服务器上。在这种情况下,数据库分配始终是主库。有关详细信息,请参阅单主库

operational view new
图 1. 集群架构

数据库主库

主库 (Primary) 是参与写入操作处理并可以作为该数据库写入者的数据库副本。一个数据库可以在集群中拥有一个或多个主库分配。

只有主库有资格担任数据库的写入者。在任何给定时间,只有一个主库会自动被选为数据库的写入者。写入者可能会随时间而变化。

数据库写入者会将写入操作同步推送到其他主库,并且在收到数据已写入足够多成员的确认之前,不允许完成提交。

为了实现高可用性,请创建具有多个主库的数据库。如果不需要高可用性,可以创建一个仅包含单个主库的数据库,以实现最低的写入延迟。

如果太多的主库失效,数据库将无法再处理写入操作并变为只读状态。

主库提供:

  • 向数据库写入数据的能力(具有可选的容错性)。

  • 从数据库读取数据的能力。

  • 针对不同故障场景的容错能力。

容错性通过以下公式计算:
M = 2F + 1,其中 M 是容忍 F 个故障所需的主库数量。

总的来说,容错性是指在不影响特定操作的情况下可以丢失的主数据库副本数量。例如,如果只有一个主库副本,则没有容错能力,因为它一旦下线,就无法提供服务。如果您有两台服务器,每台都拥有数据库的主库副本,当一台下线时,另一台仍然拥有数据副本,因此读取可用性将得到保留。

故障类型的容错能力,按丢失后果从轻到重排序如下:

  • 写入可用性:
    如果丢失了写入可用性,您的数据库将无法再接受任何写入。

  • 读取可用性:
    如果丢失了读取可用性,您的数据库将无法再提供任何读取服务。

  • 持久性:
    如果丢失了持久性,则写入数据库的数据会丢失,您需要从备份中恢复数据库。

在此语境下,诸如关闭 Neo4j 进程以升级二进制文件或将服务器下线进行维护等操作都包含在故障中,因为它们使数据库副本变得不可用。

如果您希望在不中断服务的情况下进行升级,则需要容错能力。

因此,您至少需要三个主库(以及三个用于托管每个副本的服务器),才能在其中一个成员发生故障时保持写入可用性。这对大多数部署来说已经足够了。

如果您希望在两个主库成员发生故障时仍能保持写入可用性,则需要五个主库

您可以拥有的最大主库数量是 九个,但不建议设置那么多主库。因为主库越多,每次写入操作需要联系的服务器就越多,这可能会增加写入延迟。

数据库从库

从库 (Secondary) 是通过事务日志传送从主库异步复制的数据库副本。从库会定期检查上游数据库成员是否有新事务,并将其传输到本地。

数据库从库的主要目的是扩展读取工作负载。从库充当图形数据的缓存,可以执行任意只读查询和过程。

多个从库可以从较少数量的主库获取数据,从而实现查询工作负载的显著分布,提高可扩展性。数据库可以拥有相当数量的从库。

从库的丢失不会影响数据库的可用性;但会降低查询吞吐量。它也不会影响数据库的容错性。

虽然从库充当数据库的副本,提供一定程度的持久性(已提交的内容不会丢失),但它们并不完全保证这一点。从库根据自己的计划从选定的上游成员拉取更新。由于其异步性质,从库可能会暂时滞后于主库,这意味着最近提交的事务可能不会立即在从库上可见。

从库通常提供读取扩展,即如果您的读取查询量超过了主库的处理能力,您可以添加从库来分担负载;或者配置查询路由,使读取请求优先针对从库,从而让主库专注于处理写入工作负载。

因此,对于应该拥有多少个从库没有硬性规定。通常的做法是从零开始,不断增加,直到读取性能和集群稳定性达到可接受的水平为止。

您可以拥有的最大从库数量是 20 个

system 数据库的主库与从库

记录 DBMS 中存在哪些数据库的 system 数据库也可以处于主库或从库模式。但是,与标准数据库不同,它不是通过 Cypher 命令来配置拓扑的。相反,它由 server.cluster.system_database_mode 设置控制。

在决定拥有多少个 system 数据库主库和从库,以及由哪些服务器托管时,请遵循以下准则:

  • 稳定、长寿命的服务器是托管 system 主库的良好选择,因为预计它们会保持在线,并且在需要时可以被主动关闭。

  • 临时或经常变动的服务器是托管 system 从库的良好选择,因为它们可能更频繁地被添加或删除。

  • 单个 system 主库无法为 system 数据库的写入提供容错能力。因此,在典型的集群部署中,最好从三个 system 主库开始,以确保写入可用性。

  • 虽然 system 数据库的写入量很低,且可以容忍更高的写入延迟(允许超过九个 system 主库),但通常不建议这样做。

数据库拓扑示例

有关跨多个数据中心部署集群的信息,请参阅设计具有弹性的多数据中心集群

单主库

如果您只有一个数据库副本,那么它就是主库。所有的读写操作都通过该副本进行。如果该副本不可用,则无法进行读写。如果该副本的磁盘丢失或损坏,持久性就会丧失,您必须从最新的全量备份中恢复。

三个主库

在拥有三个主库的集群中,成员会选举出一个领导者来处理写入操作。每次写入在被视为已提交(持久化)之前,都会被复制到至少一个额外的主库中。这确保了如果任何单个主库发生故障,更新仍然可以在另一个成员上获得。即使数据库副本完全丢失,包括磁盘无法恢复,也是如此。

follower writer
图 2. 三个主库与客户端之间的通信

如果一个主库副本发生故障,数据库在剩余两个主库的辅助下仍可进行写入,但它不再具备写入可用性的容错能力。

另一次故障将导致无法处理任何新的写入,直到恢复其中一个成员或使用新成员重建数据库。不过,数据库在最后一个成员上仍可提供读取服务。

非写入者主库也提供读取能力和容错能力。默认情况下,读取查询会被路由到非写入者节点(参见 dbms.routing.reads_on_writers_enabled)。

参见 用户数据库主库的地理分布,了解跨三个数据中心部署三个主库集群的模式。

五个主库

如果您希望获得超过一个任意数据库成员的容错能力,请部署五个主库。您将能够容忍任何两个主库的故障。其余的三个主库仍然可以保持法定人数(quorum)并确保数据库继续运行。

有关跨多个数据中心部署五个主库的信息,请参阅 弹性多区域集群部署 → 设计具有弹性的多数据中心集群

单主库加从库

如上所述,单主库无法为写入可用性或持久性提供容错能力。如果单主库发生故障,则无法处理写入操作;如果其磁盘丢失,则最近的更新可能会丢失。

然而,添加一个或多个从库意味着即使在主库丢失的情况下,也可以保持读取可用性。

请记住,从库可能没有最新的数据,最新的数据仅保证存在于主库上。

从库通常处理所有的读取查询(参见 dbms.routing.reads_on_writers_enabled)。

三个主库加从库

如上所述,三个主库为写入可用性和持久性提供了容错能力。从库和非写入者主库都可以处理读取查询。不过,如果您希望主库专注于写入工作负载,可以配置仅从库来处理读取(参见 dbms.routing.reads_on_primaries_enabled)。

丢失任何单个数据库副本都不会影响写入可用性、读取可用性或持久性。

如果所有从库都发生故障,那么不担任写入者的主库将开始处理读取查询,以维持读取可用性。

因果一致性

虽然集群的操作机制在应用程序看来很有趣,但思考应用程序如何使用数据库来完成工作也很有帮助。在许多应用程序中,通常需要从图中读取数据并向图中写入数据。根据工作负载的性质,通常需要读取操作考虑到之前的写入,以确保因果一致性。

因果一致性是分布式计算中使用的众多一致性模型之一。它确保因果相关的操作在系统中的每个实例上都以相同的顺序被看到。因此,无论客户端应用程序与哪个实例进行通信,都能保证读到其自身的写入内容。这简化了与大型集群的交互,允许客户端将其视为单个(逻辑)服务器。

因果一致性使得向主库模式的服务器写入数据,并从从库模式的服务器(其中图形操作进行了横向扩展)读取这些写入内容成为可能。例如,因果一致性保证了创建用户帐户的写入操作在同一用户随后尝试登录时就已经存在。

在执行事务时,客户端可以请求一个书签(bookmark),并将其作为参数提供给后续事务。使用该书签,集群可以确保只有那些已经处理了客户端书签事务的服务器才会运行其下一个事务。这提供了一个因果链,从客户端的角度确保了正确的读后写(read-after-write)语义。

除了书签外,其他一切都由集群处理。数据库驱动程序与集群拓扑管理器协作,选择最合适的服务器来路由查询。例如,将读取操作路由到数据库从库,将写入操作路由到数据库主库。