Petabyte-Scale数据集的Neo4J性能冒险

2021-03-07 11:32:54

我最近提出了一个新的研究项目理念:让我们拍摄所有github(或<插入您的首选VCS主机>),并创建所有代码的多语言(偶数语言无话)具体语法树,以便我们可以一些其他不可能难以进一步的研究和回答令人难以置信的复杂问题。

最初我使用MongoDB开始该项目并将节点之间的引用存储为ObjectID,但是我很快意识到表格格式不足以能够有效地表示真正的树。

因此,我将整个项目转换为我遇到的第一个和最重要的图形数据库:neo4j。

正如我快速了解到新的数据库范式,我也很快了解到我之间存在很多问题,并将数百个数据的数百个数据插入单个图形......

第一个设计使用Neomodel来控制布局并定义图形连接,实际上我们仍然使用Neomodel用于收集过程的较低性能敏感部分。

随着Neomodel运行一个相当长的时间,显而易见,当我用CProfile和Snakeviz检查收集器过程时,它的开销太空了。

使用Neomodel有多次要求的查询是简单地插入一个语法节点(WSTNode),额外的检查基数和多个语句只能在新创建的节点上设置属性非常慢:尽管我发现我可以插入几百个每秒使用128个进程的节点。

从刚刚开始的少数谷歌结果,很清楚,写自己的原始Cypher查询是下一步的下一步,以便更好地控制所需的操作。

来自这些努力的下一个公关将WSTNode创建例程重新建成了一系列函数,每个功能都有一个不同的任务:创建节点,连接节点,添加节点文本,连接节点文本等。

与使用Neomodel相比,这确实有所改善,但现在主要的瓶颈真的在网络插座上等待。

这些5个块中的每一个对应于在插入WSTNode期间使用的5个功能之一,注意到它们是大致相同的大小导致我的下一个改进......

一旦我看到每个查询才能初始化连接并读回结果,我都应该减少查询总数,这真的很容易。

而不是创建节点,返回ID,运行新查询以使用该ID连接...只是重写Create查询,以连接新节点,这意味着较少的整个查询少执行!

所以现在我们减少了每节点的呼叫数量超过一半的时间,我们看到了合理的改进,但我们仍然只在每秒谈论几百个节点,即使是100多名工人。

现在我们处于困难的问题。因为我们插入彼此引用的节点集合,我们无法并行迭代它们,否则某些节点可能不会将其父节点写入数据库。

因此,为了执行此操作,我需要向WSTnode结构添加另一个属性:预订。此属性对于文件中的节点是唯一的,并且在将WSTnode插入数据库之前计算,这意味着我们可以使用此属性来引用Neo4j的节点ID之前引用节点。

一旦我们在文件中唯一地引用节点的方法,我们就可以使用超神奇的展开Cypher语句批量大量节点插入。基本上这允许我们通过一个巨大的查询参数(如列表中的10,000个节点的属性)并执行单个查询以创建这些10,000个节点。

如果到目前为止,任何经验丰富的Neo4j用户遵循,他们可能已经意识到,我的WSTNodes上的预订属性在图中不是唯一的,因此即使我们在该属性上有索引,当您的图表成长到大小的Terabytes时,您将拥有太多的节点,其中具有相同的预订,以有效地搜索它们,以查找与我们在工作中相同的WSTFile节点的关系的全部。

由于此单个额外的条件在Where子句内,Neo4j数据库定期将我们的实验室服务器的所有128个硬件线程放在WSTNodes中,只需查找与我们WSTFile关系的关系。

因此,我需要一种方法来引用我刚刚使用恒定时间查找的节点。我一整天都在思考如何克服这个问题,最后最终接受了只要执行两个查询,而不是继续敲击Cyper语法Quirks会更快。

第一个查询后,我使用返回的创建节点ID来执行第二个查询中的常量时间关系。可能有某种方式基于展开的结果创建关系,但只要我们在DB端的常数插入我很开心。

绩效改进怎么样?我们从330秒到3岁以下!当然,单个文件330秒首先是荒谬的。

我对大量批量大小的初始猜测是每次查询约为10,000个节点,但是当我们乘以128(并行运行的进程)和RAM使用的保守估计时,我们正在寻求尝试在每种千兆字节的多个千兆字节上处理内存中的事务第二。

对此的解决方案是在Neo4J.conf中手动设置内存设置,因为我有一个专门的大量系统,专门用于研究工作,我可以窃取相当多的内存:

#MemRec的设置必须更改有点vs推荐的内容:#我需要至少128克为我自己的代码运行在同一系统DBMS上。记忆。堆。 initial_size = 31 g dbms。记忆。堆。 max_size = 128 g dbms。记忆。 PageCache。 size = 259500 m#它也建议将内存错误转换为完全崩溃,而不是允许部分崩溃的数据库继续运行:DBMS。 JVM。附加= - xx:+ ExitOnoutofMemoryError

neo4j.exceptions.transientError:{code:neo.transienterror.transaction.bookmarkTimeout} {消息:数据库' top1k'不达到所要求的版本:113071.最新数据库版本是113054}

这是一个棘手的,我仍然没有完全固定在发生这种情况的确切条件下,但它归结为数据库不及时应用交易,这意味着当前的“Live版本”可能超过一个几个交易日期。 (具有我的数据规模,它从日期的5-20个版本范围内。)

我可以提出这个问题的最好的解决方案是只运行一半的工作进程,我的猜测是数据库一次遇到128次连续交易。

我想知道为什么这一切恰好发生,如果可能的话,我会更喜欢将查询失速,直到满足请求的版本(如果无法达到该版本,而不是稍后失败 在。 如果您碰巧了解某些内容或如何发生这种情况,请留言,电子邮件给我发电子邮件,甚至在我们的项目中打开问题!