【问题标题】:Is the Python object mapping that I'm doing for neo4j too naive?我为 neo4j 做的 Python 对象映射是不是太天真了?
【发布时间】:2018-11-27 16:55:42
【问题描述】:

我正在寻找一些关于如何将应用程序代码重写为非幼稚的一般建议,或者是否放弃 neo4j 以使用另一种数据存储模型。这不仅仅是“主观的”,因为它与 Python 中 neo4j 驱动程序的具体、正确使用以及它执行我的代码方式的原因显着相关。

背景:

我和我的团队一直在使用 neo4j 来存储最初存储在 Python 对象中的图形友好数据。最初,当地/内部专家建议我们使用 neo4j,因为它似乎符合我们的数据存储和操作/查询要求。数据始终是一组精心构建的本体的特定实例。例如(伪数据):

Superclass1 -contains-> SubclassA
Superclass1 -implements->SubclassB
Superclass1 -isAssociatedWith-> Superclass2
SubclassB -hasColor-> Color1
Color1 -hasLabel-> string::"Red"

...等等,以创建一些相当复杂和冗长的层次结构。

对于原型设计,我们使用 RDFLib 将这些数据存储为语法三元组(主语->动词/谓语->宾语)的序列,并使用 RDFLib 的图形生成器构建图形。

现在,由于这些信息只是一个复杂的层次结构,我们只需将其存储在一些自定义 Python 对象中。我们这样做也是为了向需要与我们的核心服务交互的其他开发人员提供一个简单的 API。我们给他们一个 Python 库,它是我们的 Object 模型,让他们用数据填充它,或者,我们填充它并将其交给他们以便于阅读,然后他们用它做他们想做的事。

为了永久存储这些对象,并希望加速这些数据的写入和读取(查询/过滤),我们构建了自定义对象映射代码,该代码利用官方 neo4j python 驱动程序来写入和读取这些 Python 对象,递归地,往返于 neo4j 数据库。

问题:

对于大型和复杂的数据集(例如 15k+ 节点和 15k+ 关系),我们代码的对象关系映射 (ORM) 部分太慢,而且可扩展性很差。但我和我的同事都不是数据库或 neo4j 方面的专家。我认为我们对如何完成这个 ORM 太天真了。我们开始怀疑使用 neo4j 是否有意义,而更传统的 ORM(例如 SQL Alchemy)可能只是更好的选择。

例如,我们现在的ORM提交算法是一个递归函数,可以像这样提交一个对象(伪代码):

def commit(object):
    for childstr in object:             # For each child object
        child = getattr(object, childstr)   # Get the actual object

        if attribute is <our object base type): # Open transaction, make nodes and relationship
            with session.begin_transaction() as tx:
                <construct Cypher query with:
                MERGE object            (make object node)
                MERGE child             (make its child node)
                MERGE object-[]->child  (create relation)
                >
                tx.run(<All 3 merges>)

            commit(child)                   # Recursively write the child and its children to neo4j

这样做是不是很幼稚?尽管我们是定制的,像Py2neo's OGM 这样的 OGM 库会更好吗?我见过this 和类似的问题推荐这种或那种OGM 方法,但在this 文章中,它说根本不要使用OGM。

我们真的必须实现每一种方法和性能基准吗?似乎必须有一些最佳实践(除了使用不适合我们用例的batch IMPORT)。我们已经阅读了类似链接的文章,并看到了编写更好查询的各种技巧,但在尝试逐行优化代码之前,最好退后一步,更一般地检查案例。虽然很明显,我们可以在一定程度上改进 ORM 算法。

使用像这样的递归策略向 Neo4j 写入和读取大而深的分层对象是否有意义? Cypher 中有什么东西,或者我们缺少的 neo4j 驱动程序吗?还是使用 Py2neo 的 OGM 之类的东西更好?最好完全放弃neo4j吗? neo4j 和 Cypher 的优势不容忽视,而且我们的数据确实似乎很适合图表。谢谢。

【问题讨论】:

    标签: python database neo4j cypher object-relational-model


    【解决方案1】:

    如果不查看所有代码并了解类层次结构,很难知道,但目前我冒险猜测您的代码在 OGM 位中很慢,因为每个关系都是在其自己的事务中创建的。因此,您正在为更大的图表执行大量事务,这会减慢速度。

    我建议您在创建每个类/对象时进行初始导入,而不是仅仅添加一个新的或编辑一个类的关系,而是使用您的类检查器来简单地创建一个图形表示数据,然后使用 Cypher 在 Neo4J 中以更少的事务构建它。使用一些基本的拓扑图论,您还可以通过减少需要执行的查找次数来优化它。

    您可以在您的 python 代码中创建一个NetworkX MultiDiGraph 来为您的类的结构建模。从那里开始,有几种不同的策略可以将数据放入 Neo4J - 我也刚刚找到 this,但不知道它是否有效或效率如何。

    查询以导入图表的最有效方式将取决于图表的拓扑结构,以及它是否是循环的。下面是一些选项。

    1.在两组查询中创建图表

    为每个节点标签运行一个查询以创建每个节点,然后另一个以在每个节点标签组合之间创建每条边(这样做的效率将取决于您使用的不同节点标签的数量)。

    2。从图中的拓扑最高点或最低点开始,将图创建为一系列路径

    如果你有很多不同的边标签和节点标签,这可能需要编写大量的密码逻辑,结合 UNWINDFOREACH (CASE r.label = 'SomeLabel' THEN [1] ELSE [] | CREATE (n:SomeLabel {node_unique_id: x})-&gt;,但如果图形非常分层,你也可以使用 python 来跟踪哪个节点已经创建了所有较低的节点和关系,然后使用该知识来限制在查询中发送到 Neo4J 的路径的大小。

    3.使用 APOC 导入整个图

    另一个可能适合也可能不适合您的用例并且可能会或可能不会更高性能的选项是使用 NetworkX 将图形导出到 GraphML,然后使用use the APOC GraphML import tool

    再次重申,如果不查看您的所有数据,很难提供精确的解决方案,但我希望这对引导正确方向有所帮助!很乐意根据更多数据帮助/回答任何其他问题。

    【讨论】:

    • 已更新以使用 GraphML 添加新的可能解决方案
    • 谢谢。我们的数据确实是相当分层的,您似乎在说最好尝试优化我已经制作的 ORM。与具有非常长的查询字符串的大型事务相比,多笔事务的成本是多少?看起来现在,构建一个扁平化的 ORM 意味着做后者。
    • 再一次,如果没有特定的用例,很难正确回答,但总的来说,Cypher 是一种非常强大的语言,它与 Neo4J 的交互得到了很好的优化。例如,如果您通过层次结构迭代以非常定义的模式创建大量节点/关系,那么在 Cypher 中执行此操作可能比通过重复请求和事务执行此操作效率高几个数量级(也许通过网络)来创建单独的边缘,而这首先不包括事务性的固有好处......
    • 另外,关于调整现有的 O(R/G)M 或做一些不同的事情,我建议最好的方法是将你的数据从你的实现中抽象到你的数据库中。如果您可以将数据存储在 NetworkX 中,那么您就更有能力将其放入任何数据存储中(尽管听起来 Neo4J 可能是您最好的选择)。编写代码来创建数据,然后编写代码来存储它!这样就更容易测试,如果您决定将来更改为其他东西,您只需更改一个做一件事的较小模块,而不是一个做太多事情的大模块。
    【解决方案2】:

    这里发生了很多事情,所以我会尝试用较小的问题来解决这个问题

    像 Py2neo 的 OGM 这样的 OGM 库会更好吗

    对于任何 ORM/OGM 库,现实情况是,您始终可以通过绕过它们并深入研究野兽的腹部来获得更好的性能。不过,这并不是 ORM 的全部工作。 ORM 旨在通过简化相对高效的 DB 使用来节省您的时间和精力。

    所以这取决于,如果你想要最好的性能,跳过 ORM,把时间花在尽可能低的水平上(*需要你正在使用的野兽的高级低级知识,以及很多你的时间)。否则,ORM 库通常是您最好的选择。

    我们的代码太慢了,而且扩展性很差

    数据库很复杂。如果可能的话,我建议让某人成为公司范围内的数据库管理员/专家。 (如果您还没有一个审查新员工实际上知道他们在说什么的人,这就更难了)

    假设这不是一个选项,这里有一些事情需要考虑。

    • IO 很昂贵。尤其是通过网络。最小化必须在任一方向发送的数据。 (这就是页面返回结果的原因。只返回您实际需要的数据)
      • 请注意,创建请求连接非常昂贵。尽量减少对数据库的调用。 (享受平衡两者的乐趣^_^)(注意:ORM 通常具有内置机制,只提交已更改的内容)
    • 快速获取您想要的数据。在数据库中创建索引以大大提高获取速度。 id 越独特和一致越好。
      • 警告,索引必须在写入更改其中的值时更新。因此索引会降低写入速度并消耗更多内存来获得读取速度。最小化索引。
    • 事务是一种内存操作。提交事务是磁盘 IO 操作。这就是批处理作业效率更高的原因。
      • 警告,内存不是无限的。保持您的工作规模合理。

    您可能已经知道,将数据库操作扩展到生产级别并不有趣。在任何轴上过度优化都太容易烧伤自己,而这只是表面上的简化。

    对于原型设计,我们将这些数据存储为语法三元组的序列

    少一个问题,多一个陈述,但不同类型的数据库有不同的优势和劣势。无方案的数据库更专门用于缓存存储;图数据库专门用于基于关系(边)的查询;关系数据库专门用于获取/更新记录(表); Triplestores 更专业于三元组 (RDF); (等等。还有更多类型)

    我之所以提到这一点,是因为听起来您的数据可能主要是“一次写入,多次读取”。在这种情况下,您可能实际上应该使用 Triplestore。您可以将任何数据库类型用于任何事情,但选择最佳数据库需要您了解如何使用数据,以及这种使用可能如何演变。

    我们真的必须实现每一种方法和性能基准吗?

    嗯,这就是为什么存储过程如此重要的部分原因。 ORM 有助于抽象这部分,并且拥有内部领域专家会非常有帮助。可能只是您正在突破 1 台机器所能做的极限。也许您只需要升级到集群;或者您可能有可怕的代码效率低下,当没有(或 1 个)值更改时,您在 1 次保存操作中触摸节点 10k 次。不过老实说,除非您知道自己在寻找什么,否则基准测试并没有多大作用。例如,通常 5 小时和 0.5 秒之间的差异可能就像创建 1 个索引一样简单。

    (公平地说,虽然购买更大更好的数据库服务器/集群可能是低效的解决方案,但与 1 个数据库管理员的薪水相比,它有时是最具成本效益的。所以,再次,取决于你的优先级。我'确定你的老板可能会优先考虑与你想要的不同)


    TL;DR

    您应该聘请领域专家来帮助您。

    如果这不是一个选项,去书店(或谷歌)拿起 Databases 4 傻瓜(动手学习数据库在线教程课程),并自己成为领域专家。 (你可以用它来提升你对公司的价值)

    如果您没有时间这样做,那么您唯一的节省可能就是升级您的硬件以使用蛮力解决问题。 (*只要增长不是指数级的)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-02
      • 1970-01-01
      • 2012-05-30
      • 1970-01-01
      • 1970-01-01
      • 2013-01-26
      相关资源
      最近更新 更多