【问题标题】:create unique and merge slow创建独特并缓慢合并
【发布时间】:2014-04-07 23:10:06
【问题描述】:

我正在使用一个 neo4j 数据库,该数据库由大约 275,000 个英语单词组成,这些单词与它们所包含的字母相关联。我在 Windows 上运行 Neo4j 2.0.1 Community Edition。

我正在尝试使用以下 Cypher 将新单词节点插入图中,更新这些节点上的属性,然后在新添加单词节点时创建与现有(字母)节点的新关系:

BEGIN
MATCH (A:Letter {token:"A"}),
(B:Letter {token:"B"}),
(C:Letter {token:"C"}),
(D:Letter {token:"D"}),
(E:Letter {token:"E"}),
(F:Letter {token:"F"}),
(G:Letter {token:"G"}),
(H:Letter {token:"H"}),
(I:Letter {token:"I"}),
(J:Letter {token:"J"}),
(K:Letter {token:"K"}),
(L:Letter {token:"L"}),
(M:Letter {token:"M"}),
(N:Letter {token:"N"}),
(O:Letter {token:"O"}),
(P:Letter {token:"P"}),
(Q:Letter {token:"Q"}),
(R:Letter {token:"R"}),
(S:Letter {token:"S"}),
(T:Letter {token:"T"}),
(U:Letter {token:"U"}),
(V:Letter {token:"V"}),
(W:Letter {token:"W"}),
(X:Letter {token:"X"}),
(Y:Letter {token:"Y"}),
(Z:Letter {token:"Z"})
// Create Words and link to proper letters
MERGE (w1:Word {string:"WHOSE", length:5})
ON MATCH SET w1.s_enable1=TRUE
ON CREATE SET w1.s_enable1=TRUE
// create the letter->word relationships if necessary
CREATE UNIQUE (w1) <-[:IN_WORD {position:1}]- (W)
CREATE UNIQUE (w1) <-[:IN_WORD {position:2}]- (H)
CREATE UNIQUE (w1) <-[:IN_WORD {position:3}]- (O)
CREATE UNIQUE (w1) <-[:IN_WORD {position:4}]- (S)
CREATE UNIQUE (w1) <-[:IN_WORD {position:5}]- (E)
MERGE (w2:Word {string:"WHOSESOEVER", length:11})
ON MATCH SET w2.s_enable1=TRUE
ON CREATE SET w2.s_enable1=TRUE
CREATE UNIQUE (w2) <-[:IN_WORD {position:1}]- (W)
CREATE UNIQUE (w2) <-[:IN_WORD {position:2}]- (H)
CREATE UNIQUE (w2) <-[:IN_WORD {position:3}]- (O)
CREATE UNIQUE (w2) <-[:IN_WORD {position:4}]- (S)
CREATE UNIQUE (w2) <-[:IN_WORD {position:5}]- (E)
CREATE UNIQUE (w2) <-[:IN_WORD {position:6}]- (S)
CREATE UNIQUE (w2) <-[:IN_WORD {position:7}]- (O)
CREATE UNIQUE (w2) <-[:IN_WORD {position:8}]- (E)
CREATE UNIQUE (w2) <-[:IN_WORD {position:9}]- (V)
CREATE UNIQUE (w2) <-[:IN_WORD {position:10}]- (E)
CREATE UNIQUE (w2) <-[:IN_WORD {position:11}]- (R)
... N-2 more of these ...;
COMMIT
... M-1 more transactions ...

我正在使用 neo4j-shell 执行像这样的 Cypher 命令文件来添加新单词。大多数被合并的词已经存在于图中。只有一小部分是新的。

此代码通常可以工作,除了:(a) 它运行非常缓慢(例如,当 N = 50 时,50 秒/50 字事务),以及 (b) 当需要创建新关系时(使用 CREATE UNIQUE),事务缓慢到很多分钟,偶尔会失败并出现错误“超出 GC 开销限制”。

我还尝试使用 MERGE 代替 CREATE UNIQUE。这通常以类似的方式工作(非常慢),并在运行多个事务后最终因 Java 堆内存错误而失败。 (似乎是某种内存泄漏。)

任何关于我做错了什么和/或完成这项任务的更好方法的见解将不胜感激。

更多信息

此图主要是提供一个动手原型,以帮助理解 Neoj4 在感兴趣领域的特性和功能:语言结构、单词统计、对文字游戏有用的查询(填字游戏、拼字游戏、与朋友的单词、刽子手、. ..)。

所有属性都已编入索引(在 neo4j.properties 文件和 CREATE INDEX ON 命令中)。

s_enable1 表示要添加的单词列表的来源。在这种情况下,“enable1”字典(173,122 个单词)。初始图表是使用“sowpods”词典(267,751 个单词)创建的。 s_ 前缀代表“来源”。每次将新字典添加到图表时,都会创建一个新属性来指示哪些单词(现有的和新的)与每个列表相关联。 (例如,单词 AA 出现在 sowpods 和 enable1 字典中,因此 AA 单词节点的 s_sowpods 和 s_enable1 属性都设置为 TRUE。)

MERGE 或 CREATE UNIQUE 似乎非常适合在添加新字典时不断更新图表。

sowpods 构建创建了大约 250 万个(字母)-[:IN_WORD]->(单词)关系。 enable1 合并可能会再创建 500 K 左右。 (很多 enable1 词都很长,例如 16 - 21 个字母。)

操作系统是 Windows 7。运行 Java 7.51 x64。 (最初运行的是 x32,它慢了 2 倍。) java -XshowSettings 显示 885.5 M 最大堆。我相信数据库设置大多是默认设置。 (哪些设置特别突出?)

【问题讨论】:

  • 你真正想要实现什么?您是否为匹配/合并操作创建了约束/索引? s_enable1 是什么意思?为什么你也将它设置为匹配?你的数据库中有多少关系?你的数据库设置是什么,你在什么操作系统上运行?请使用所有这些信息更新您的帖子。
  • 如果你在没有任何参数的neo4j-shell中运行它,它也必须解析每个大语句。可怜的 Cypher 解析器,尝试创建只匹配他们需要的字母的更小的语句。此外,您所有的字母都将是图中的超级节点,因此您可能需要考虑查看 2.1。如果将创建关系语句放入 on create 子句中,则不需要创建唯一性。
  • 感谢您的意见!不确定如何使用 neo shell 参数化我的 Cypher 脚本。 (将需要阅读。)我想知道 SET ON CREATE 构造是否允许在创建新单词节点时有条件地创建新/缺失的链接。似乎只适用于属性创建。仍然不确定如何重组事物以完成我需要的那种 MERGE 更新。欢迎提供更多提示!
  • 我还将s_enable1=true 更改为 source="enable1" 和source="sowpods" 应该更容易处理。
  • 在 Windows 上增加堆大小(例如增加到 3 或 4G),因为 Neo4j 内存映射发生在堆内部。节点和 rel 的目标计数是多少?

标签: performance neo4j cypher


【解决方案1】:

您不必对第一部分进行参数化,但您需要一个索引/约束:

create constraint on (l:Letter) assert l.token is unique;
create constraint on (w:Word) assert l.string is unique;

要在 shell 上进行参数化,您可以这样做:

export word=WHOSE

MATCH (w:Word {string:{word}}) RETURN w;

很遗憾,Neo4j 的拆分操作还不能对空拆分字符串起作用。

否则可能会出现这样的情况:WITH split({word},"") as letters

MERGE (w:Word {string:{word}, length:length({word})})
   ON CREATE SET w.s_enable1=TRUE
FOREACH (i in range(0,length({word})-1) | 
  MERGE (l:Letter {token:substring({word},i,1)})
  MERGE (l)-[:IN_WORD {position:i}]->(w)
)

不带参数的具体例子:

MERGE (w:Word {string:"STACKOVERFLOW", length:length("STACKOVERFLOW")})
   ON CREATE SET w.s_enable1=TRUE
FOREACH (i in range(0,length("STACKOVERFLOW")-1) | 
  MERGE (l:Letter {token:substring("STACKOVERFLOW",i,1)})
  MERGE (l)-[:IN_WORD {position:i}]->(w)
)

你可以在这里试试:http://console.neo4j.org

【讨论】:

  • 这很酷。非常优雅和快速!但是 :-( ... 并不能解决我的向已经存在的 WordGraph 添加新单词的问题。我需要ON MATCH SET w.s_enable1=TRUE 来设置预先存在的单词的 s_enable1 属性并制作没有其他添加。如果单词已经存在(并且具有其他属性,例如 s_sowpods=TRUE),则最后的 MERGE (l)-[:IN_WORD {position:i}]-&gt;(w) 语句似乎会导致添加冗余关系。任何想法如何防止形成这种冗余关系?
  • 误报。 :IN_WORD(position) 属性在我的原始数据库中被索引为 1 - N。您的代码索引为 0 - N-1。通过将最后一个命令更改为MERGE (l)-[:IN_WORD {position:i+1}]-&gt;(w) 来解决所有问题。谢谢!
【解决方案2】:

以下是我的经验和提示:

  1. 尽可能使用BatchInserter。它要求数据库处于离线状态,并且您以限制性的方式构建代码,但如果您能做到这一点,您将获得回报。
  2. 将您的数据库放在RAM disk (tmpfs) 上。这在我的系统上大约有 7.5 倍的速度提升,从 HDD 上的 ~200 CREATE/s 到 ~1500 CREATE/s。 SSD 的速度可能会稍微低一些,但根据我的经验,HDD→SSD 的改进对于 Neo4j 来说并不那么重要。因为我的数据库目前大约 4 GiB,而我只有 8 GB RAM,所以我处于困境中,但即使有潜在的虚拟内存交换tmpfs,性能也比 HDD+Linux 缓冲区/缓存好得多。
  3. 限制 Cypher 查询大小,我的最佳选择是每个 Cypher 查询 10-50 MERGE。超过 50 MERGEs,它会变慢。在 Cypher 查询中大约 500-1000 MERGEs,Neo4j 抛出 StackOverflowError
  4. 最多使用number_of_CPUs - 2 线程来运行 Neo4j 事务,将 1 个线程用于主线程,另一个用于 Neo4j 自己的事务写入。这导致我的 i7-3770K 上所有 8 个逻辑内核的 CPU 利用率约为 95%。
  5. 在运行前清除索引,并在所有操作完成后创建它们。当您需要索引时,创建您自己的内存中(即ConcurrentHashMapImmutableMap 更好),但要确保相应地调整堆大小。即使您的堆进入虚拟内存,它仍然比 Neo4j 的事务刷新更快。
  6. 如果可能,请事先创建所有索引节点。这意味着您在创建关系时不必关心唯一性,您可以使用MATCH 而不是MERGE。对于内存索引,这意味着即使不能使用ImmutableMap,您也可以使用HashMap 而不是ConcurrentHashMap,即使是多线程。

类似帖子:

  1. Neo4j 2.0 Merge with unique constraints performance bug?
  2. How to improve performance for massive MERGE insert?
  3. Neo4jClient - Merge within ForEach with 1000 very slow (Unique Constraint)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    相关资源
    最近更新 更多