Neo4j应用程序体系结构

目录

Application Architecture

嵌入式与服务器

服务器模式

聚类

 复制

使用队列写入缓冲区

全球集群

负载平衡

将读流量和写流量分开

阅读自己的文章


Application Architecture

在规划一个基于图形数据库的解决方案时,有几个架构决策。根据数据库产品的不同,这些决定会略有不同我们选择。在本节中,我们将描述一些体系结构的选择,以及相应的应用架构,在使用Neo4j时提供给我们。

嵌入式与服务器

目前,大多数数据库作为服务器运行,通过客户机库访问。Neo4j是有点不寻常的是,它既可以在嵌入式模式下运行,也可以在服务器模式下运行——事实上, 追溯到近10年前,它的起源是作为嵌入式图形数据库。

嵌入式数据库与内存数据库不同。
Neo4j的嵌入式实例仍然使所有数据在磁盘上持久。稍后,在第85页的“测试”部分,我们将讨论非永久性的GraphDatabase,它是Neo4j的内存版本,专为测试而设计。

在嵌入式模式中,Neo4j与我们的应用程序运行在同一个进程中。嵌入式Neo4j非常适合硬件设备、桌面应用程序,也非常适合整合到我们自己的应用服务器中。嵌入式模式的一些优点包括:
低延时
因为我们的应用程序直接与数据库对话,所以没有 络开销。
选择的api
我们可以访问创建和查询数据的所有API:核心API、遍历框架和Cypher查询语言。
显式事务
通过使用核心API,我们可以控制事务的生命周期,在单个事务的环境中对数据库执行复杂的命令序列。Java api还公开了事务生命周期,使我们能够插入自定义事务事件处理程序,对每个事务执行额外的逻辑。
然而,在嵌入式模式下运行时,我们应该记住以下几点:

仅支持Java虚拟机
Neo4j是一个基于jvm的数据库。因此,它的许多api只能通过基于jvm的语言访问。
GC行为
在嵌入式模式下运行时,Neo4j受主机应用程序的垃圾收集(GC)行为的影响。长时间的GC暂停会影响查询时间。
此外,当一个内嵌的实例作为HA(高可用性)集群的一部分运行时,长时间的GC暂停会导致集群协议触发。
数据库生命周期
应用程序负责控制数据库生命周期,包括安全启动和关闭它。嵌入式Neo4j可以通过集群实现高可用性和水平读取扩展,就像服务器版本一样。事实上,我们可以运行嵌入式实例和服务器实例的混合集群(集群是在数据库级执行的,而不是在服务器级执行的。这在企业集成场景中很常见,在这种场景中,来自其他系统的定期更新会对嵌入式实例执行,然后复制到服务器实例。

服务器模式

在服务器模式下运行Neo4j是目前部署数据库最常用的方法。每个服务器的核心都是一个嵌入式的Neo4j实例。服务器模式的一些好处包括:

REST API
服务器公开了一个丰富的REST API,允许客户机通过HTTP发送json格式的请求。响应由json格式的文档组成,文档中添加了超媒体链接,这些超媒体链接宣传了数据集的其他特性。REST API可由终端用户扩展,并支持Cypher查询的执行。
平台独立性
由于访问是通过通过HTTP发送的json格式文档进行的,所以运行在任何平台上的客户机都可以访问Neo4j服务器。所需要的只是一个HTTP客户端库。
扩展的独立
Neo4j运行在服务器模式下,我们可以独立于应用服务器集群扩展数据库集群。

与应用程序GC行为的隔离
在服务器模式下,Neo4j受到保护,不受应用程序其余部分触发的任何不良GC行为的影响。当然,Neo4j仍然会产生一些垃圾,但是它对垃圾收集器的影响在开发过程中已经被仔细地监视和调优,以减轻任何重大的副作用。然而,由于服务器扩展使我们能够在服务器内运行任意Java代码(参见第78页的“服务器扩展”),使用服务器扩展可能会影响服务器的GC行为。
在服务器模式下使用Neo4j时,我们应该记住以下几点:
络开销
每个HTTP请求都有一些通信开销,尽管它相当小。在第一个客户机请求之后,TCP连接一直保持打开状态,直到客户机关闭。
事务状态
Neo4j服务器有一个事务性Cypher端点。这允许客户端在单个事务的上下文中执行一系列Cypher语句。对于每个请求,客户端都延长了交易的租期。如果客户机由于任何原因未能完成或回滚事务,则该事务状态将保持在服务器上,直到超时(默认情况下,服务器将在60秒后回收孤立事务)。对于需要单一事务上下文的更复杂的多步骤操作,我们应该考虑使用服务器扩展(参见第78页的“服务器扩展”)。

与应用程序GC行为的隔离
在服务器模式下,Neo4j受到保护,不受应用程序其余部分触发的任何不良GC行为的影响。当然,Neo4j仍然会产生一些垃圾,但是它对垃圾收集器的影响在开发过程中已经被仔细地监视和调优,以减轻任何重大的副作用。然而,由于服务器扩展使我们能够在服务器内运行任意Java代码(参见第78页的“服务器扩展”),使用服务器扩展可能会影响服务器的GC行为。
在服务器模式下使用Neo4j时,我们应该记住以下几点:
络开销
每个HTTP请求都有一些通信开销,尽管它相当小。在第一个客户机请求之后,TCP连接一直保持打开状态,直到客户机关闭。
事务状态
Neo4j服务器有一个事务性Cypher端点。这允许客户端在单个事务的上下文中执行一系列Cypher语句。对于每个请求,客户端都延长了交易的租期。如果客户机由于任何原因未能完成或回滚事务,则该事务状态将保持在服务器上,直到超时(默认情况下,服务器将在60秒后回收孤立事务)。对于需要单一事务上下文的更复杂的多步骤操作,我们应该考虑使用服务器扩展(参见第78页的“服务器扩展”)。

如前所述,对Neo4j服务器的访问通常通过其REST API进行。
REST API由HTTP上的json格式文档组成。使用REST API,我们可以提交Cypher查询,配置命名索引,并执行几个内置的图形算法。我们还可以提交json格式的遍历描述,并执行批处理操作。对于大多数用例,REST API已经足够了;然而,如果我们需要做一些目前使用REST API无法完成的事情,我们应该考虑开发一个服务器扩展。

服务器扩展
服务器扩展使我们能够在服务器内部运行Java代码。使用服务器扩展,我们可以扩展REST API,或者完全替换它。
扩展采用JAX-RS注释类的形式。JAX-RS是一个用于构建RESTful资源的Java API。使用JAX-RS注释,我们修饰每个扩展类,以指示服务器它处理哪些HTTP请求。附加注释控制请求和响应格式、HTTP头和URI模板的格式。

服务器扩展可以成为我们应用程序体系结构中的强大元素。他们的主要好处包括:

复杂的事务
扩展使我们能够在单个事务的上下文中执行任意复杂的操作序列。
选择的api
每个扩展都注入了对服务器核心的嵌入式图形数据库的引用。这使我们能够访问所有的API——核心API、遍历框架、图算法包和密码——来开发我们的扩展行为。
封装
因为每个扩展都隐藏在RESTful接口后面,所以我们可以随着时间的推移改进和修改它的实现。
响应格式
我们控制响应-包括表示格式和HTTP头。这使我们能够创建响应消息,其内容使用我们域的术语,而不是标准REST API的基于图的术语(例如,用户、产品和订单,而不是节点、关系和属性)。此外,在控制附加到响应的HTTP头时,我们可以利用HTTP协议进行缓存和条件请求等操作。

在考虑使用服务器扩展时,我们应该记住以下几点:

JVM只
与使用嵌入式Neo4j进行开发一样,我们必须使用基于jvm的语言。
GC行为
我们可以在服务器扩展中做任意复杂(和危险)的事情。我们需要监控垃圾收集行为,以确保不会引入任何不利的副作用。

聚类

正如我们在164页的“可用性”中详细讨论的那样,Neo4j使用主从复制实现高可用性和水平读取扩展。在本节中,我们将讨论使用集群Neo4j时需要考虑的一些策略。

 复制

尽管对集群的所有写操作都是通过主服务器协调的,但是Neo4j确实允许通过从服务器进行写操作,但是即使这样,要写的从服务器在返回到客户机之前也要与主服务器同步。由于附加的 络流量和协调协议,通过从服务器进行写入可能比直接写入主服务器慢一个数量级。写作通过奴隶唯一原因是增加耐用性的保证每个写(写是由耐用的两个实例,而不是一个),并确保我们可以阅读自己写使用缓存分片(见83页“缓存分片”和“读自己写“84页在本章后面)。因为Neo4j的新版本允许我们指定将对主机的写入复制到一个或多个从服务器,从而增加了对主机的写入的持久性保证,所以通过从服务器写入的情况现在不那么引人注目了。现在,建议将所有写操作都定向到主服务器 ha.tx_push_factor and  ha.tx_push_strategy configuration settings。

使用队列写入缓冲区

在高写负载的场景中,我们可以使用队列来缓冲写操作并调节负载。
使用这种策略,对集群的写入被缓冲在一个队列中。然后,worker轮询队列并对数据库执行批量写操作。这不仅可以调节写流量,还可以减少争用,并允许我们在维护期间暂停写操作而不拒绝客户端请求。

全球集群

对于迎合全球用户的应用程序,可以在多个数据中心和亚马逊Web服务(AWS)等云平台上安装多区域集群。多区域集群使我们能够从集群中离客户端最近的区域读取数据。然而,在这些情况下,区域的物理分离所带来的延迟有时会破坏协调协议。因此,通常最好将总统连任限制在一个地区。为了实现这一点,我们为不希望参与主选举的实例创建仅从数据库。我们通过包括ha来做到这一点。slave_coordinator_update_mode=无实例配置中的配置参数。

负载平衡

当使用集群图数据库时,我们应该考虑跨集群的负载平衡,以帮助最大化吞吐量和减少延迟。Neo4j没有包含本机负载平衡器,而是依赖于 络基础设施的负载平衡功能。

将读流量和写流量分开

考虑到建议将大部分写流量都指向master,我们应该考虑清楚地将读请求和写请求分开。我们应该配置负载均衡器,将写流量直接发送到主机,同时在整个集群中平衡读流量。
在基于web的应用程序中,HTTP方法通常足以区分具有显著副作用的请求(对服务器没有显著副作用的write-from请求):POST、PUT和DELETE可以修改服务器端资源,而as GET则没有副作用。
在使用服务器扩展时,重要的是区分使用@GET和@POST注释的读和写操作。如果我们的应用程序仅仅依赖于服务器扩展,那么这就足以将两者分开。但是,如果我们使用REST API向数据库提交Cypher查询,情况就不是那么简单了。
REST API使用POST作为读取和写入Cypher请求的通用“处理此”语义。为了在这个场景中分离读和写请求,我们引入了一对负载均衡器:一个是始终将请求定向到主服务器的写负载均衡器,另一个是在整个集群中平衡请求的读负载均衡器。在我们的应用程序逻辑中,我们知道操作是读操作还是写操作,然后我们必须决定对于任何特定的请求应该使用哪一个地址,如图4-9所示。
在服务器模式下运行时,Neo4j会公开一个URI,该URI指示该实例当前是否是主实例,如果不是,那么哪个实例是主实例。负载平衡器可以每隔一段时间轮询这个URI,以确定将流量路由到哪里。

用于实现一致路由的策略因域而异。有时进行一些粘性训练就足够了;其他时候,我们希望基于数据集的特征进行路由。最简单的策略是让首先为特定用户服务请求的实例,然后为该用户的后续请求服务。
其他特定于领域的方法也可以工作。例如,在地理数据系统中,我们可以将关于特定位置的请求路由到已经为该位置预热过的特定数据库实例。这两种策略都增加了所需节点和关系已经缓存在主内存中的可能性,在主内存中可以快速访问和处理它们。

阅读自己的文章

有时,我们可能需要读取自己的写入——通常是当应用程序应用最终用户更改,并需要在下一个请求中向用户反映此更改的影响时。虽然对主机的写操作是立即一致的,但是整个集群最终是一致的。我们如何确保直接写到主机上的写被反映到下一个负载均衡的读请求中解决方案是使用缓存分片中使用的一致的路由技术,将写操作引导到将用于为后续读操作提供服务的从机。这假定可以根据每个请求中的某些域条件一致地路由写和读。
这是少数有意义的通过奴隶进行写入的场合之一。但是要记住:通过从属器写入比直接写入主器要慢一个数量级。我们应该谨慎地使用这一技术。如果我们的写操作中有很大比例要求我们读取自己的写操作,那么这种技术将显著影响吞吐量和延迟。

文章知识点与官方知识档案匹配,可进一步学习相关知识Neo4j技能树首页概览1640 人正在系统学习中

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年1月14日
下一篇 2021年1月14日

相关推荐