创建独特和合并缓慢

时间:2014-04-07 23:10:07

标签: performance neo4j cypher

我正在玩一个neo4j数据库,该数据库包含〜275,000个与其包含的字母相关联的英文单词。我在Windows上运行Neo4j 2.0.1社区版。

尝试使用以下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),事务处理缓慢到很多分钟,偶尔会因错误而失败&#34;超出GC开销限制&#34;。

我也尝试使用MERGE代替CREATE UNIQUE。这通常类似地工作(非常慢),并且在运行多个事务之后最终因Java Heap内存错误而失败。 (看起来像某种内存泄漏。)

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

更多信息

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

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

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

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

sowpods build创建了大约250万(字​​母) - [:IN_WORD] - &gt;(word)关系。 enable1合并可能会创建另外500 K左右。 (许多enable1单词很长,例如16 - 21个字母。)

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

2 个答案:

答案 0 :(得分:4)

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

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的拆分操作在空拆分字符串上不起作用。

否则会出现类似的情况:将split({word},&#34;&#34;)作为字母

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

答案 1 :(得分: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 MERGE秒,它会变慢。在Cypher查询中大约500-1000 MERGE s,Neo4j抛出StackOverflowError
  4. 使用最多number_of_CPUs - 2个线程来运行Neo4j事务,为主线程留下1个线程,为Neo4j自己的事务写入留下另一个线程。这导致i7-3770K上所有8个逻辑核心的CPU利用率达到约95%。
  5. 在运行之前清除索引,并在所有操作完成后创建它们。当您需要索引时,创建自己的内存(即ConcurrentHashMap,但ImmutableMap更好),但请确保相应地调整堆大小。即使你的堆进入虚拟内存,它仍然比Neo4j的事务性刷新更快。
  6. 如果可能,请事先创建所有索引节点。这意味着您在创建关系时无需关心唯一性,并且可以使用MATCH代替MERGE。对于内存中索引,这意味着即使无法使用ImmutableMap,即使使用多线程,也可以使用HashMap而不是ConcurrentHashMap
  7. 类似帖子:

    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)