Neo4j的不可变图

问题

想象一下,您必须管理数百个不同的项目,这些项目可能具有多个不同的动作或构造块。 这些项目可以分为“监视组”-您分配了一些人来执行它们,而其他人则被分配来监视他们。 您拥有“成本中心”,进度和绩效指标以及所有与之相关的管理要素。 它必须足够通用以涵盖不同类型的业务,但是所有这些都需要管理项目。

这是我们正在研究的一些大型CAC-40(巴黎泛欧交易所)上市公司使用的一种复杂工具。 该软件堆栈经历了许多转型和快速开发,以涵盖新功能并获得更多通用架构。

具有参与者的异步

为了获得速度和灵活性,它使用关系数据库作为持久层将其重写为Scala和Akka,并且越来越多的数据处理已在应用程序代码中进行。

数据流看起来像这样:

虽然这是横向扩展的通常方法-将数据库降级到简单的键值存储,并将数据处理到更好的分布式应用程序节点中-但这也将很多复杂性移到了业务层,同时取消了对数据库的可见性和一致性检查。存储中的业务数据。 尽管Scala对于中间层几乎是一种完美的语言,但它并不能解决与“手动”执行“我们的”代码中的所有事情相关的复杂性问题。

拥有如此简单的存储意味着您不能像典型的SQL或NoSQL文档数据库中那样浏览,查询或“检查”该数据。 如果您需要任何临时的业务分析/智能,则必须在应用程序层中完成。 企业之所以喜欢SQL和与SQL相关的技术,是有原因的,这是探索现有数据的简便性。 在这里,所有新查询都必须编码为API,同时会增加错误修复和维护成本。

客户端还有一个愿望-所有更改都变得不可变,以便及时进行查询和比较。 这是现有模型不准备的。 虽然将其改进为具有“撤消”值是一个简单的步骤,但是添加“撤消关系”将意味着每次发生此类更改时都会对整个结构进行快照。 这样,在几周内我们将不得不管理数百个可用于查询的数据库“快照”。 如果没有专业的Dev / Ops团队,那么完全不做就算了,即使要处理多个不同的数据库,也不容易且经济高效。

也许我们应该尝试图数据库?

第一阶段是评估如何以更有用的形式表达此类数据。 当时市场上有2–3个可行的图形数据库选项(OrientDB甚至是Titan)可用,但是经过一番检查并了解到我们在可能的项目风险/失败方面的预算非常有限,我们决定仅使用Neo4j。 对于此类数据和大小,它是最成熟的解决方案,覆盖了语言驱动程序/ api和doc,活跃社区和确定许可(例如MongoDB)。 Neo4j使用的Cypher查询语言类似于SQL,但涵盖了连接数据点的功能。 所有这些都对具有可接受风险水平的项目带来了希望,可以带来新的期望功能。 我们之前已经拥有Neo4j的使用经验,因此开始很容易。

如何为数据建模-如何查询数据?

在Neo4j中为图形数据建模的重要事项是正确建立模型。 您不会第一次获得它,因为您真正需要的数据库结构是什么,当您将现有查询转换为Cypher(Neo graph SQL)或db-api调用并执行它们并获得一些基准时,就会看到数字。 这更像是快速的原型检查-修复周期

Ruby / Neography中的一个小型ETL在快速更改数据模型如何从SQL转换并存储在Neo4j中方面大有帮助。 这样一来,旧SQL模型中现有的每个客户端自定义项也可以清除并转换为更灵活的通用图形模型,而不会污染新的Scala中间层逻辑。

Neo4j带有一个方便的命令行和一个不错的Web数据浏览器,因此您可以轻松地可视化所拥有的内容:

密码-图形查询语言

通常,图具有通过关系或边连接的“节点”或“顶点”。 Neo4j中的单个节点可能像SQL世界中的小表一样,具有自己的带有值的字段。

Cypher-[[Neo4j http://neo4j.com/developer/cypher-query-language/中使用的图形查询语言],可以描述您要选择的关于节点及其之间的连接的术语。 基本语法实际上非常简单自然。

假设您有两个节点,一个需要执行一些操作的项目

为此,我们可以编写类似于SQL的查询:

实际上可能有人在工作:

该操作可以同时属于某个“无线R∧D”成本中心,而项目则由某些PM管理:

这是用两行写的,但是当可视化时,我们将在图片的中心有一个“动作”节点。

让我们复杂化:类似于lambda的时间机器结构,具有不变的数据

我们的图可以表示当前时间“现在”的执行项目的连接和进度图。 我们已经具有保留节点值先前更改的功能,例如action.name,但是当我们的操作移至另一个项目时,我们没有任何可追踪的东西。 我们需要一种体系结构来覆盖整个数据结构的不变性。 我们可能会问“三个月前我们的项目和任务看起来如何”,或者“谁搬了那个项目,我们节省了全球R∧D成本的10%以上”? 也许我们只是希望在一段时间内进行一些汇总,以查看进度如何进行。

答案是将可变的更改与不可变的“核心”分开,并对所有更改加时间戳记,其中,数据更改既是节点属性的更改,又是它们之间的连接。

这个想法可以用这样的模型来表达:

P1和P2是“数据实体的不变核心”。 P1有4个更改,因此它们被存储为状态序列S1-S4。 P2有两个更改S1-S2。 在某个时间点,连接P1和P2的关系r1的属性已更改,从而创建了另一个关系r2。

当然,关于如何存储时间相关数据还有更多答案-值得这样做(您自己的研究| https://www.google.com/search?q = neo4j + with + time + dependent + data) ,还有许多其他示例。

回溯建模:索引编制和约束

在对新结构进行建模时,我们需要考虑的一些问题是-我们将如何使用这些数据,对其进行查询,数据库如何帮助我们保持其健康,独特和快速找到? Neo4j具有处理节点数据的索引-它们具有节点标签和节点属性,并且可以约束为一对唯一值-与我们从SQL数据库中了解到的类似:

还有“旧版”索引,在

  conf / neo4j.properties 

(或使用Java API),它的底层和后台Lucene引擎更接近一点,这使我们有可能对图形中的几乎所有属性进行索引,甚至可以进行全文本索引。 只需对数据使用适当的索引编制方案,我们就可以将查询时间减少10倍。 使用Neo4j的较新版本,还提供了探查器和查询解释语法,以查看如何优化查询和/或改进索引。

提供新的Scala API

软件堆栈分为多个部分,其中的UI用Html5 / JS / Angular编写,后端API用Scala / Play / Akka编写。 目标是提供新的后端,同时使UI api调用保持完整。 随着数据源和“查询引擎”发生了巨大变化,这还意味着需要对返回的数据进行一些额外的修改,以与以前完全相同的方式获取它们。 这里的另一个复杂性是因为它已经在模拟以前的版本时增加了类似SOAP的查询,而其中一个子目标是引入简单的REST调用。

开发此堆栈时,Scala工具箱的有用部分是类型别名(以更好地表达已使用的复合结构),将简单但特定的数据值打包到其自己的案例类中,并且当处理模型快速更改时-使用类型之间的隐式转换以最小化第一步,对现有代码所做的更改会产生影响,并且无需过多重构即可提供新功能。

另一个故事是关于Scala标准集合,折叠,可选/部分数据的选项。 此外,Scalaz库的某些部分通过提供简单的操作来转换和合并类似类动物的数据结构,也非常有帮助。

从一开始就开发的关键部分是以BDD风格编写的集成测试,该测试涵盖了新引擎快速增长的复杂性以及可能的操作。 概括地说,在Scalatest的帮助下,另一层功能测试涵盖了HTTP API调用和JSON结构/转换。

Scala和Neo4j

选择最好的库时,即使Neo4j的某些部分是用Scala编写的,Scala-Neo4j的空间也不会给太多空间。 使用本机Java连接感觉就像样板一样。 FaKod库是其自己的DSL的一个有趣选择,但是它也迫使学习“ Cypher的另一个版本”,这可能导致游戏“如何在类似Cypher的缩小的DSL中编写这样的Cypher语句”,因此众所周知。使用一些类似于SQL / ORM的库。 合适的选择似乎是AnormCypher,它可以执行任何种类的Cypher查询,但也需要仔细引用和解析返回的数据。 值得一提的是对集合类型中Scala-Java转换的不断了解,因为当它们“泄漏”到代码的其他部分并带有令人惊讶的错误消息时,有时会很乏味。

AnormCypher实际上有一个处理缺陷,导致堆问题-REST客户端读取的所有数据在传递给应用程序之前,都会在内存中转换为更多类似表的结构。 当一个节点可以具有多个关系时,这是非常有问题的,这样就为每个数据点创建了多个“行”(请考虑SQL中的笛卡尔联接)。 对于某些查询,即使整个数据库的大小约为15MB,查询响应数据也可能增长10倍,而其他响应数据可能会增长10倍。 这导致烦人的JVM崩溃,内存池为3–4 GB,而在处理数据量方面却很少。

另一个问题是某些查询的延迟。 我们已经获得了完全有效的复杂查询,但是它们的执行时间低于客户的期望。 通过混合数据模型,我们解决了这两个问题-为简单的点对点连接添加了缓存层-这样,可以从Neo4j获取更复杂的“根”数据,同时可以从缓存中快速填充其他信息。

进一步改进的空间

数据模型和设计中有许多地方可以进一步改进。 图数据库的想法大相径庭,但存在相似的问题和挑战。

  • 数据将如何使用以及在何处以及以多快的速度增长?
  • 我们关心所有变化吗? 从时间角度来看,我们对当前数据比对历史数据更为敏感,因此旧的更改可能会由于时间周期而部分变平/“压缩”,以保持存储的合适性。

Neo4j的整体经验

Neo4j是一种易于上手的工具,但其分析可能性非常复杂。 当处理更多的连接数据时,它可以大大简化业务逻辑,提供快速的工具来“查看”和“触摸”数据。 它为探索现有数据开辟了一个新领域,这是一种“更高层次的抽象”,给人一种类似于有人将平面文件与完整SQL数据库进行比较的感觉。

编写Cypher查询以原型化和开发新报告,尝试一些新的分析方法并获得新的见解很简单。 隐藏的复杂性可能偶尔出现-Neo4j方法来表示图结构和遍历,图索引,具有可选匹配项的查询或使用REST或自己编写的服务器扩展之间的差异。 还有一些“灰色”地方,例如Neo4j简单备份,实际上创建了Neo4j导入工具无法直接使用的文件。 另一方面-Neo4j通过更轻松的操作,导航和可视化,为现有数据增加了新价值。

Neo4j已经被越来越多的大型公司使用。 最近,它也被用作围绕“巴拿马泄漏”进行调查的新闻工具。

也许它对您的连接数据项目也有帮助?