图的行业应用图应用架构设计

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 图的行业应用图应用架构设计

1、高层Neo4j架构

网络异常,图片无法展示
|

① 硬盘

避免硬盘输入输出是最大化Neo4j性能的关键因素之一,当不能避硬盘输入输出时使用高速硬盘能提高性能。

② 空间大小

可以基于对需求节点、关系和属性的估计做粗略的计算。一般公式是:核心图形大小(字节)=(节点数×以字节记的节点存储大小)+(关系数×以字节记的关系存储大小)+(属性数×平均每属性的字节数)此外还会有索引、事务日志等需要的额外硬盘占用。

③ 存储文件

Neo4j存储图形数据库的不同部分在一组存储文件(store file)中。这些存储文件通常通过记录类型分解——有节点、关系、属性、标签等单独文件。这些文件中的结构和数据布局做了专门的设计和优化以提供高效的存储格式,Neo4j运行时引擎可以利用这些格式提供在图形数据库中高效查找和遍历。核心启用之一是Neo4j按照免索引邻近原则存储数据。

每一个节点在数据库中的存储有一个直接的链接或引用到它的临近节点(这些节点直接与它相连),并且必须不需要使用任何其他的辅助结构(例如索引)找到它们。

通过这种方式存储数据,当执行遍历时,Neo4j能够直接跟随指针连接节点和关系,当试图定位直接连接的节点时,在Neo4j中不会产生开销。免索引邻近原则允许查询时间与你想搜索多少图形数据成比例,而不是与整个图形尺寸成比例。存储文件位于主图形数据库目录中(默认是服务器的data/graph.db)并以neostore作为前缀。

网络异常,图片无法展示
|

注:*块尺寸可以使用string_block_size和array_block_size的参数进行设置。默认块的大小是120字节和用于管理的8个字节。

主存储文件具有一个固定的或统一的记录尺寸(14字节用于节点、33字节用于关系等)。除在快速查找和遍历中起重要作用,固定的长度使得规划和计算需要多少硬盘容量和内存变得容易。

固定长度记录如何改进性能?

使用固定长度的记录意味着基于节点或关系编号的查找不需要通过存储文件本身的搜索。而是给定一个节点或关系编号,可以直接计算出文件中存储数据的起始点。节点存储文件内的所有节点记录都是14字节的长度。节点和关系编号是数值型的并在存储文件内直接与它们的位置相关。节点编号1将是第一个记录,节点编号1000将是第1000个。

如果你想查找与节点编号1000相关的数据,你将能够计算出这一数据将会在节点存储文件中从14000字节开始(14字节×节点编号1000)。计算这些数据起始位置的时间复杂度O(1)远远小于搜索一个函数的时间复杂度 O(log n)。固定记录的大小在为计算一个文件中相邻记录块的存储位置提供一个快速机制中也扮演着重要角色,文件系统缓存中也使用这一快速机制加载存储文件的部分到缓存中。

当涉及存储文件的结构时,有两个应该注意的重要特性。

第一个是这些固定记录大小可以用作帮助估计中心图形文件将占据的硬盘空间,因此,你的硬盘就需要有这么大。第二个是这些存储记录在涉及文件系统缓存和它是如何工作时,具有1∶1的映射。

④ Neo4j缓存

Neo4j使用一个两层缓存策略:文件系统缓存和对象缓存。

网络异常,图片无法展示
|

文件系统缓存:

文件系统缓存是自由内存(还没有分配给任何处理流程中的内存)的一个区域,是操作系统保留的提高文件读写速度的内存。文件系统缓存使用一种称作内存映射输入输出的方法(memory-mapped IO)。每当一个处理请求一个文件时,文件系统缓存检查是否已经调入。如果没有调入,从硬盘中读到内存区域。之后对同一文件的需求就可以直接从内存中访问,通过减少对物理硬盘输入输出的需求增加性能。数据的变化也会写到文件系统缓存,而不是直接写到物理硬盘。性能的提升是通过访问文件系统缓存获得的,而不是直接访问一个旋转的硬盘,这会大约快500倍。操作系统负责管理这些内存,包括决定什么时间将缓存中的数据变化直接写到物理硬盘中去。尽管操作系统管理这些数据什么时间读出和写入这个缓存区,处理过程也可以请求某些文件或部分的文件调入这一缓存区进行处理。

Neo4j利用操作系统的这一特点(通过Java NIO包)有效地从存储文件中加载、读出和写入。这提供了Neo4j中的第一层(有时称作低层)缓存功能。

如果系统崩溃并且数据只写入内存中而没有写入硬盘将会发生什么情况?

Neo4j使用分离的、持久的事务日志来确保实际写入到硬盘文件每一次提交事务的实施。每当一个提交发生时,尽管存储的文件本身可能还没有实际更新,事物日志总会在硬盘上有数据。然后在系统崩溃后重启时可以使用事务日志恢复数据。换句话说,这一事务日志可以用作重建存储文件,使之与上一次提交时完全一样,并且存储的文件也会与系统崩溃前把数据直接写入硬盘的文件一样。

配置文件系统缓存:

假设你有一个顶层Neo4j数据库目录大约占据了2.2G的容量,有下列的存储文件和大小:

网络异常,图片无法展示
|

下面的程序给出了将会确保整个图形数据库能够调入到内存放在文件系统缓存中的配置。这个配置文件的设置显示了大约2.5G的内存,允许数据大约有10%的增加。

网络异常,图片无法展示
|

当以服务器模式运行Neo4j时,这些设置是在conf目录中的 neo4j.properties文件中指定的。

对嵌入式模式的设置,配置设置可以在数据库创建时传入,或者通过引用一个neo4j.properties文件,可以从classpath的某个地方访问该文件,或者直接通过映射在Java中引用。

当我们讨论HA时,我们也应该注意缓存分区的概念,这个概念可以用于帮助加载图形数据的适当部分到多个计算机的内存中。

默认设置

在缺少用户的配置文件时,Neo4j将会试图根据系统自己的知识及给予它的可用内存数量确定怎样的配置是最好的。

对象缓存:

Neo4j的对象缓存是JVM堆里面的一个区域,Neo4j在那里通过核心Neo4j API以一种便于用于遍历和快速恢复的优化形式存放Java对象的节点和关系版本。对象缓存是另一个大大超过文件系统缓存提高性能的策略。在对象缓存中访问数据(相对于到文件系统缓存)大约快5000倍。

配置对象缓存:

配置对象缓存有两个方面。第一个涉及配置JVM,尤其是JVM堆,这可以通过在启动时给JVM提供适当的-Xmx???m参数来实现。第二种方法是选择一种适当的缓存类型,这是通过在neo4j.properties文件中设置cache_type 参数控制的。

JVM负责对你的对象分配合适的内存,以及通过使用一个垃圾收集器清理没有引用的对象。垃圾收集器可以调整和配置,但是,一般情况下,JVM堆尺寸大于8GB时似乎会引起问题。大的堆尺寸能够导致长GC暂停和抖动——这正是需要避免的浪费时间的类型。长GC暂停和抖动对性能具有可怕的影响,因为当JVM结束时将试图执行清理和释放对象的维护活动,这要比应用程序执行任何有用工作花费更多的时间。因此,应该分配多少内存给JVM以便Neo4j运行的性能最佳?直到找到一个合适的利用JVM堆空间尽可能多而没有引起太多的与GC相关问题的协调点之前,这是你需要考虑的问题之一。利用以下的启动参数和数值提供一个好的开始点。

·分配尽可能多的内存给JVM堆(考虑你的计算机和JMV特定的限制

——对大多是人来说是6GB或更少),通过-Xmx???m参数传递。

·以-server标志启动JVM。

·使用并发标记和清扫压缩垃圾收集器而不是默认配置的JVM,使用-

XX:+UseConcMarkSweepGC参数。

按照官方文档的对象缓存类型选项:

网络异常,图片无法展示
|

注:*默认缓存类型

**仅在Neo4j的企业版中可用。(注:一些用户已经成功的使用这类缓存至堆尺寸多达200G)随着JVM堆尺寸的不断增加,它几乎要占用了所有可用的内存,堆尺寸的设置和文件系统缓存的设置应该放在一起考虑。记住也要包括其他的因素,例如,操作系统对内存的需求和其他任意运行的流程。如果你是在以嵌入式模式运行,Neo4j数据库将会与你的主机应用程序共享JVM堆,因此,应确保也考虑这个因素。

缓存方式可以总结如下:

·最低目标是设置将全部或尽可能多的图形调入文件系统缓存中。

·然后,尽量多使用对象缓存。

⑤ 事务日志及可恢复性

所有的事务日志文件可以在Neo4j数据库的顶层目录中找到,并且具有以下格式的文件名nioneo_logical.log.*。

当Neo4j启动时,它要做的第一件事是查阅最近的事务日志,并且重做发现的所有针对实际存盘文件的所有事务。有可能这些事务已经应用到了存盘文件中(记住是操作系统控制什么时间文件系统缓存写入硬盘,这可能已经发生或没发生)。然而,重新再一次做这些事务不会出现问题,因为这些动作被看作为幂等的,即这些变化可以被多次应用而不会导致图形数据库中的不同结果。

⑥ 编程API

在Neo4j结构的最顶层是三个主要的API(Cypher、遍历和核心),用于访问和处理Neo4j中的数据。

网络异常,图片无法展示
|

如果性能是最重要的,你或许会选择深入底层使用核心API,因为这会提供最灵活的性能和对图形交互的控制。这是有代价的,因为这需要对图形数据的布局有一个准确的把握,以便与之交互,并且这种交互可能是相当详细的。

2、Neo4j高可用

Neo4j使用主从复制结构,广义地说,提供了支持两个关键特色的能力:在硬件故障时的应变能力和容错能力,扩展Neo4j读密集型数据场景的能力。

① Neo4j集群概述

Neo4j的实施是基于Paxos协议,它主要处理主选择,但是Neo4j也负责处理一般的基于集群的管理任务。

在一个Neo4j HA集群中,当每一个数据库实例遇到逻辑需求与群中的。其他成员进行协调和通信时,其自身是能够自给自足的。当一个HA数据库实例启动时,要做的第一件事情是建立到它的配置集群的连接。

neo4j.properties文件包含不同的属性来控制HA配置(都以ha*为前缀),包括属于本集群中成员的开始设置列表中的属性——ha.initial_hosts。

如果启动后不存在集群,意味着这是第一台试图连接集群的计算机,然后这个过程将会建立开始的集群,并将这台计算机作为集群的群主。

网络异常,图片无法展示
|

所有的写请求最终都是通过主计算机完成的,尽管也有可能把请求发送到从计算机去执行。然而,这样做要比直接由主计算机做慢得多。在这种情况下,从计算机在逻辑上内置了确保同步首先写到主计算机中,然后再写到其自身。数据的修改通过一个可配置的基于推拉机制的从计算机传回主计算机。

以集群设置运行对Neo4j提供的ACID保证具有一定影响。Neo4j的单机一致性担保放宽了在一个集群中的最终一致性。

② 设置Neo4j集群

按照这些步骤做初始设置:

1)创建一个根目录,例如~/n4jia/clusterexample。

2)下载Neo4j的企业版并解压到这一文件夹。

3)重新命名这个文件夹为machine01,并且复制这个文件夹及其所有的内容到目录machine02和machine03。你的结果文件结构应该像这样:

网络异常,图片无法展示
|

4)在每一台计算机的根目录内,找到conf/neo4j-server.properties文件并确保以下的属性设置:

网络异常,图片无法展示
|

网络异常,图片无法展示
|

按照以下步骤启动与验证你设置的服务器。

1)在每一个目录中发出如下启动命令启动服务器:

网络异常,图片无法展示
|

2)对每一个服务器用浏览器访问网页管理控制台一次,需要一定的时间启动以验证服务器正确启动并探测它提供的详细情况:

网络异常,图片无法展示
|

③ 复制—读和写的策略

与一般的所有写请求都通过主机而读请求通过从机的主-从复制设置不同,Neo4j能够处理来自于集群中任何实例的写请求。同样,Neo4j能够处理来自于任何主机、从机任何实例的读请求。

写请求的处理根据直接来自于主机还是通过从机而不同:

网络异常,图片无法展示
|

任何直接由主机收到的写请求将会以非常相同的方式作为一个标准的非HA事务处理;数据库需要一个锁定并执行一个局部事务。唯一的差别是在主机上提交以后,有一些配置属性(ha.tx_push_factor和ha.tx_push_strategy)额外地以乐观的方式把事务推入到n(默认为1)个从机。至于乐观的方式,是指主机尽最大的努力确保事物传送至配置的从机中去,但是如果这种传送不成功,不管是由于什么原因,主机上的原始事务仍不会失败。

其他从机也能够周期性地从主机拉入做最新的更新。这是通过设置 ha.pull_interval属性为适当的值实现的,例如2s(每2秒)。这一推拉机制用于控制Neo4j在集群中最终具有一致性的速度。

转到第二种情况,如果一个从机收到一个写请求,事件的不同顺序将会发生:

网络异常,图片无法展示
|

指向从机的写入依赖于可用的主机以便更新的实现。这是因为从机在继续执行前将会同时需要在主机和从机获得锁。Neo4j具有可靠的、内置的逻辑自动探测什么时间什么原因使实例(包括主机)潜在消失或不能访问。在这样的情况下,一个新的主机会被选出,并且如果需要,尽可能多的达到数据最新。如果无法选出一个新的主机,不管什么原因,这会在本质上停止所有对数据库的更新。

更新总是首先应用于主机,并且仅仅当主机更新成功后,更新才应用于从机。要确保全部的一致性,Neo4j需要从机在执行任何写操作前保持最新的数据,因此,作为内部交流协议的一部分,Neo4j将会确保从机在执行任何局域写操作之前将所有的最新更新应用到自身。

·通过一个从机写入要比通过一个专门的主机写入慢得多。当通过从机写入时,由于主机和从机的同步和数据活动的协调需求的额外网络流量,将会有一个很高的延迟。

·通过一个从机写入能够增加持久性,不过这也能够通过配置主机一旦提交就复制变化实现。通过一个从机写入,可以确保总是至少有两个Neo4j 实例保持最新的数据(从机本身,以及主机)。另外,能够通过配置一旦提交,写入到主机后复制出到指定的几个从机增加持久性。尽管这会发生在一个最优基础上(通过ha.tx_push_factor属性),最后的结果是你的数据将会到你配置数量的电脑上。分布的持久性是重要的,由于这提供了更大程度上的自信,你的数据完整的保存到了多个地方,但是这也付出了代价。

现在提供给大多数客户的一般推荐是确保写入主要发送到主机,在可能的情况下,才使用ha.tx_push_*设置把数据推送至从机。这趋向于根据灵活性和性能提供最好的权衡。

④缓存分区

网络异常,图片无法展示
|

传统的分区一般涉及分裂一个整的数据集使得不同的部分存到不同的实例中。假设某些条件为真,这种方式是一种扩展大型数据库的方法,而同时随着数据的增长保持一个可预测的性能水平。

然而,图形数据库,尤其是Neo4j的最大优点在于局域的图形查询,即从给定的点开始并探索与它紧挨着周围的数据的连接模式。这可能涉及遍历非常不同的数据类型,这些数据类型会分布到不同的分区。假设你有一个这样的查询“对于顾客A,告诉我哪一个商店离她住的地方在1km以内,并且她的直接朋友在上一个月中在那个商店购物超过100英镑”。如果你决定分区数据使用户和朋友存到一个分区,零售商店在另一个单独的分区,购买历史在另一个分区,随着数据分散到不同的物理服务器,一个内置的图形查询将会结束,由于网络的交叉得到导航,会导致不可预测的和慢的数据检索时间。

直到我们能够穿越网络边界以一种可接受和可预测的方式执行如此的遍历时,在此期间涉及Neo4j的这种情况的最好解决方案是使用缓存分区。

3、Neo4j备份

① 离线备份

流程的本身相当简单,涉及以下步骤:

1)关闭Neo4j实例。

2)复制实际的Neo4j数据库文件到一个备份的位置。

3)重新开始Neo4j实例。

② 在线备份

在线备份功能仅在Neo4j的企业版中可用,但是这是一个强大和可靠的备份你的单机或集群Neo4j环境而不需要任何停机的方式。在线备份有两个选项:完全备份或增量备份。在两种情况下,对正在备份的Neo4j实例资源不需要锁定,允许它们在备份进行中继续发挥其功能。

完全备份:

一个完全备份实质上涉及复制所有的核心数据库文件到一个分离的备份目录。由于正在备份的数据库并没有锁定,很可能当备份开始后,一个事务正在运行。为了确保最后的备份保持一致性并数据最新,Neo4j将会确保这一事务和任何其他的在复制期间发生的,复制到最后的备份文件中。Neo4j 通过记录当备份开始后任何进程中的事务编号实现上述目标。当复制核心文件结束后,然后备份工具会使用事务日志。重新做从记录的事务和包括的在复制期间发生的所有事务。在这一过程的最后,你将会有一个完全的备份和一致性的Neo4j实例。

增量备份:

与完全备份不同,增量备份不复制所有的核心存储文件作为备份过程的一部分。相反,它假设至少已经做过一次完全备份,这次备份作为最初开始的点识别接下来发生的任何变化。第一次增量备份过程将仅通过从最初的完全备份发生的事务日志复制,因此使备份数据存储到一个新的上次标记的备份点。这一点是标记的,并且下一次增量备份将会使用这一点作为新的备份起始点,仅通过从最近一次增量备份点到当前的点发生的事务日志复制。这些日志在备份存储中被重新执行,使其成为最近一次的标记备份点。在此后的每次增量备份请求中都重复这一过程。增量备份是一个更高效的备份过程,由于它尽可能地最小化了通过线路传送的数据量以及实际做备份所花费的时间。

做备份的过程:

每当你的Neo4j实例开始时(嵌入式或服务器模式,单个或HA),它们检查看它们的online_backup_enabled配置参数是否设置为true。如果是true,一个分离的备份服务(来自于Neo4j发布时的一部分)也就开始了。默认的端口对单一服务器模式设置是6362,对HA设置是5001。这一服务为Neo4j备份工具的连接提供了访问点,以读取底层的存储和事务日志文件做备份。

Neo4j备份工具仅仅是一个能理解如何连接一个或多个Neo4j备份服务的

面向Java应用的UNIX或Windows脚本语言(可以在bin文件夹中找到)。备

份工具可以从网络上的任何计算机上运行,前提是这台计算机具有连接到备份服务的能力。UNIX系统的备份命令具有以下的格式:

网络异常,图片无法展示
|

参数如下:

·backup-dir 备份存储的目录。为了做完全备份,这个目录必须是空

的。如果备份工具检测到一个早期版本的数据库在那里,它将试图做增量备份。

·source-uri 这给备份工具提供了需要连接到适当的备份服务的所有信息,并且需要以下面的格式指定

网络异常,图片无法展示
|

Neo4j本身不提供任何日程功能。它期待你会使用一个外部的日程工具(如cron)来做这项工作。使用像cron类的东西,你可以设置每小时做一次增量备份,或者任何适合你的频次。

③ 从备份中还原数据

从备份中还原Neo4j数据库,只需要按照下面的步骤:

1)关闭要还原的指定Neo4j实例。

2)删除包含旧的或损坏了的版本的Neo4j数据库并用备份版本的数据库替换它。

3)重新启动Neo4j实例。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
监控 Java 持续交付
深入理解微服务架构及其在现代应用开发中的应用
深入理解微服务架构及其在现代应用开发中的应用
58 1
|
1月前
|
运维 Cloud Native 持续交付
深入理解云原生架构及其在现代企业中的应用
随着数字化转型的浪潮席卷全球,企业正面临着前所未有的挑战与机遇。云计算技术的迅猛发展,特别是云原生架构的兴起,正在重塑企业的IT基础设施和软件开发模式。本文将深入探讨云原生的核心概念、关键技术以及如何在企业中实施云原生策略,以实现更高效的资源利用和更快的市场响应速度。通过分析云原生架构的优势和面临的挑战,我们将揭示它如何助力企业在激烈的市场竞争中保持领先地位。
|
2月前
|
机器学习/深度学习 自然语言处理 分布式计算
大规模语言模型与生成模型:技术原理、架构与应用
本文深入探讨了大规模语言模型(LLMs)和生成模型的技术原理、经典架构及应用。介绍了LLMs的关键特点,如海量数据训练、深层架构和自监督学习,以及常见模型如GPT、BERT和T5。同时,文章详细解析了生成模型的工作原理,包括自回归模型、自编码器和GANs,并讨论了这些模型在自然语言生成、机器翻译、对话系统和数据增强等领域的应用。最后,文章展望了未来的发展趋势,如模型压缩、跨模态生成和多语言多任务学习。
228 3
|
8天前
|
容灾 网络协议 数据库
云卓越架构:云上网络稳定性建设和应用稳定性治理最佳实践
本文介绍了云上网络稳定性体系建设的关键内容,包括面向失败的架构设计、可观测性与应急恢复、客户案例及阿里巴巴的核心电商架构演进。首先强调了网络稳定性的挑战及其应对策略,如责任共担模型和冗余设计。接着详细探讨了多可用区部署、弹性架构规划及跨地域容灾设计的最佳实践,特别是阿里云的产品和技术如何助力实现高可用性和快速故障恢复。最后通过具体案例展示了秒级故障转移的效果,以及同城多活架构下的实际应用。这些措施共同确保了业务在面对网络故障时的持续稳定运行。
|
2月前
|
Cloud Native 安全 持续交付
深入理解微服务架构及其在现代软件开发中的应用
深入理解微服务架构及其在现代软件开发中的应用
88 32
|
2月前
|
存储 监控 API
深入解析微服务架构及其在现代应用中的实践
深入解析微服务架构及其在现代应用中的实践
76 12
|
2月前
|
运维 监控 安全
自动化运维的利剑:Ansible在现代IT架构中的应用
在数字化浪潮中,企业对IT系统的敏捷性和可靠性要求日益提高。Ansible,一种简单但强大的自动化运维工具,正成为现代IT架构中不可或缺的一部分。它通过声明式编程语言YAM,简化了系统配置、应用部署和任务自动化的过程,显著提升了运维效率和准确性。本文将深入探讨Ansible的核心特性、应用场景以及如何有效整合进现有IT环境,为读者揭示其在自动化运维中的实用价值和未来发展潜力。
|
2月前
|
监控 持续交付 API
深入理解微服务架构及其在现代应用开发中的应用
深入理解微服务架构及其在现代应用开发中的应用
32 4
|
2月前
|
运维 Kubernetes Docker
深入理解容器化技术及其在微服务架构中的应用
深入理解容器化技术及其在微服务架构中的应用
72 1
|
2月前
|
监控 持续交付 API
深入理解微服务架构及其在现代软件开发中的应用
深入理解微服务架构及其在现代软件开发中的应用
59 3