Neo4j的使用和大多数数据库一样,如果您只想查询,这很简单。使用驱动程序,创建连接,提交查询并返回结果,如此简单! 如果您正在使用数据库集群,那么要做的事情不止这些。首先,数据库实例不是单个节点,而是由多台服务器组成。本文将探讨Neo4j集群如何工作以及Neo4j驱动程序如何执行查询。 如何在Neo4j上运行查询 在讨论驱动程序之前,我们需要简单说明Neo4j集群如何工作以及集群角色是什么,这将有助于帮您了解驱动访问。
1、Neo4j因果集群 一个集群由3个或更多Neo4j实例组成,它们使用共识协议(RAFT)彼此通信,以提供容错和高可用性。在Neo4j集群中,每个实例都有完整数据库副本(该图未分片)。集群中的每个节点都有一个“role”:LEADER或者FOLLOWER。 1.1 集群角色 LEADER 负责协调集群并接受所有写入,FOLLOWER 扩展集群的读负载能力,并提供高可用性的数据。如果集群中一个节点发生故障,并且仍然拥有多数席位,则其可以继续处理读和写操作。如果失去了多数席位,那么集群只能读,并且不再具有 LEADER。 另外,集群可以增加任意数量的只读副本(READ REPLICA),它们是集群核心节点的只读副本,用于扩展查询负载。它们不是正式的集群成员,而是“tag alone”副本,可从集群同步事务。
1.2 拓扑变化 在集群的生命周期中,集群角色是临时的,而不是您配置的角色。假设您有机器 A(LEADER)、B和C。如果 A 发生故障,将在其余节点(B和C)间选举一个新的领导者。当 A 重新启动时,稍后,它将重新加入集群,但可能是 FOLLOWER。因此,角色可以在集群的整个生命周期中进行更改。集群正常运行,出于各种其他原因,集群可以选举新的LEADER ,因此,就角色本身而言,不必担心引起任何变化。 Neo4j使用RAFT共识算法来控制集群。
2、Neo4j驱动程序 驱动程序API包括以下四个关键部分,以下概念适用于Neo4j集群和单实例。 Neo4j驱动程序API的核心部分
但是,在集群中,由于我们正在与一组计算机通信,因此如何执行事务以及将事务转到何处,是“路由驱动程序”控制执行的。
3、路由驱动程序 当您使用某种 Neo4j驱动程序(Java,Javascript,Python,.Net和Go)时,会有使用 bolt+routing 协议的选项。例如,您可以连接到bolt+routing://neo4j.myhost.com。 注意: 对于集群,设置一个包含多个 A 项的 DNS 记录,每个 A 项都指向集群成员。 这样,所有客户端都可以连接到相同的 DNS 地址,但是根据集群状态和运行状况,可以将不同的计算机作为入口点。或者可以直接连接到服务器成员的 IP 地址,但是这种方式在拓扑更改时更加灵活。 如果要连接到任何单个主机或地址,则驱动程序将为您处理路由。驱动程序将首先检查数据库是否具有路由功能,如果是,则将进行读取,以保存可用读取和写入服务的全部或部分快照。在那之后,除非驱动程序与集群失去联系并且必须重新初始化路由,否则将不使用初始主机地址。
3.1 路由表 该路由表包含提供的服务器列表ROUTE,READ和WRITE功能。该路由信息被认为是volatile,并且会定期刷新。 Neo4j集群路由表
驱动程序会定期刷新此信息,因为集群拓扑可能会根据 runtime 事件(例如计算机故障)而发生变化。通常,WRITE 节点将成为 LEADER ,而 READ 节点将成为集群或只读副本中的 FOLLOWER。 重要提示: 只读副本不参与集群拓扑管理,因此它们不提供路由表,仅核心节点提供路由表。这可能在Neo4j的后续发行版中有所更改。 如果您想自己尝试一下,只需Neo4j集群任一实例执行以下语句:
CALL dbms.cluster.routing.getRoutingTable({})
YIELD ttl, servers
UNWIND servers as server
RETURN ttl, server.role, server.addresses;
Neo4j集群的路由表
这个特定的 Cypher 查询仅供内部使用,并且Neo4j的将来版本可能会更改,因此不要在其中编写代码,但可以使您了解驱动程序的实际功能。
3.2 广播地址 路由表例子中的那些地址从何而来?用户在neo4j.conf文件中配置 dbms.connector.bolt.advertised_address 。通过这种方式,Neo4j知道将哪个地址发布到可以联系到它的外部。(配置参考) 重要提示: 客户端使用广播地址访问集群,该配置非常重要,neo4j.conf显式设置。 请确保它是可以由clients解析(而不是内网地址,比如192.168.0.5)。一个常见的错误与Neo4j的连接没有配置,或将其设置为内部私有地址,外部Internet上的客户端无法连接,因为它们无法弄清楚如何访问您的数据库!
4、连接管理 一旦建立了路由表,驱动程序就可以管理与集群中所有不同计算机的连接池。用户看到的通常只是会话的创建,以及从这些会话中运行查询。驱动程序内部的实际内容看起来更像是与一组计算机(A,B等)的连接池。会话只是根据需要连接并执行查询。 Neo4j驱动程序连接管理
这些连接是分开处理的,因此,如果服务器 A 消失并且所有这些连接最终都要超时,则您在会话级别上不必一定知道。会话仍可以访问其他连接,并且您的应用程序可以继续运行。正在执行的语句将失败,并由驱动程序自动重试。 用户使用会话执行查询。会话是要创建的便宜对象(与连接不同,它的建立成本很高)。会话还提供了一种因果一致性的方式将工作链链接在一起的逻辑结构,这意味着您可以执行一系列事务,后续事务始终读取同一会话中较早事务所做的写操作。
4.1 路由查询 现在,假设您的驱动程序将查询发送到数据库,我将以JavaScript为例,此方法适用于任何语言。 我们要做的第一件事是从驱动程序中创建会话。然后,我们使用该会话执行查询。
const session = driver.session();
session.readTransaction(tx => tx.run('MATCH (n) RETURN count(n) as value'))
.then(results => {
console.log(results.records[0].get('value'));
})
.finally(() => session.close());
驱动如何知道将查询发送到哪里?在此代码示例中,我们使用了显式事务,这意味着我们告诉驱动程序我们正在执行readTransaction。我们获得了一个txTransaction对象,并以此运行查询。因为具有路由表,并且知道您正在读取,所以它最终可能会将此查询发送到具有该 READ 角色的节点之一。如果执行写事务,它将被发送到路由表的 WRITE 节点(通常是集群 LEADER)。 如果路由驱动程序在发送查询的位置上有多种选择,则使用“ least connected ”策略,这有助于避免意外路由到当前太忙而无法及时响应的服务器。 最小关联策略
4.2 自动提交事务 如果您不使用显式事务怎么办?您可以参照下面的方式:
const session = driver.session();
session.run( tx => tx.run('CALL library.myProcedure()'))
.then(results => {
console.log(results.records[0].get('value'));
})
.finally(() => session.close());
``
在这种情况下,驱动程序无法知道读或者写操作,而将事务直接发送给 LEADER 。这样,即使您没有告诉它是需要读还是写,事务也应该成功。
重要提示:
Neo4j驱动程序不会解析您的Cypher来确定您正在读取还是正在写入。
始终使用显式事务来告诉驱动是读还是写。这有助于其有效地路由查询,并从集群中获得最佳利用率。如果您从未使用过显式事务,则可能会将所有查询负载发送给 LEADER,对其进行轰炸,同时使集群中的其它计算机保持空闲状态。
5、结论
在集群部署中,Neo4j利用 bolt+routing 协议让客户端动态发现集群拓扑,并使用“least connected”策略将查询路由到集群中的计算机。所有这些对您都是透明的,因此在用户级别,您将看到的是创建会话,运行查询和使用结果。
要从此设置中获得最大收益,需要对它的工作方式有所了解,并且需要牢记的最重要部分如下:
1)正确配置广播地址
2)在驱动程序代码中使用显式事务调用
3)DNS配置尽可能创建单个DNS记录,所有客户端使用该记录访问集群任何节点
有关Neo4j路由驱动程序的更多信息,请参阅Neo4j驱动程序手册 和原文。