【问题标题】:Creating several millions of relationships in Neo4j takes a very long time在 Neo4j 中创建数百万个关系需要很长时间
【发布时间】:2021-03-03 04:35:41
【问题描述】:

我使用的是最新的 Neo4j.Driver 包 (4.2.0) 和最新的 Neo4j 服务器社区版 (4.2.3)。

我一定是做错了什么,因为我的查询需要几个小时才能完成。

我有 4 个 CSV 文件:

  1. XyzTypes.csv - 定义了 96,328 个类型节点。
  2. XyzMethods.csv - 定义了所有类型的 975,507 个方法。
  3. XyzTypeTypeDependencies.csv - 定义了 121,834 个类型-类型 DEPENDS_ON 关系。
  4. XyzTypeMethods.csv - 定义了 973,972 个类型-方法 DECLARES 关系。

下面的代码应该很简单。它只需要加载所有 CSV 并创建相应的类型、方法和关系。

这是我的代码:

var driver = GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "1"));
var session = driver.AsyncSession(o => o.WithDatabase("neo4j"));
try
{
    Console.Write("[DI");
    await session.RunAsync("DROP INDEX type_id_index IF EXISTS");
    await session.RunAsync("DROP INDEX method_id_index IF EXISTS");

    Console.Write("][C");
    await session.WriteTransactionAsync(async tx =>
    {
        await tx.RunAsync("match ()-[r]->() delete r");
        await tx.RunAsync("match (n) delete n");
        return default(object);
    });

    Console.Write("][T");
    await session.WriteTransactionAsync(async tx =>
    {
        await tx.RunAsync(@"
LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypes.csv' AS line
CREATE (:Type {
    typeId: toInteger(line.id),
    name: line.name,
    fullName: line.fullName,
    isCompilerGenerated: toBoolean(line.isCompilerGenerated),
    asmName: line.asmName
})");
        return default(object);
    });

    Console.Write("][M");
    await session.WriteTransactionAsync(async tx =>
    {
        await tx.RunAsync(@"
LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzMethods.csv' AS line
CREATE (:Method {
    methodId: toInteger(line.id),
    name: line.name,
    fullName: line.fullName,
    isCompilerGenerated: toBoolean(line.isCompilerGenerated)
})");
        return default(object);
    });

    Console.Write("][CI");
    await session.RunAsync("CREATE INDEX type_id_index FOR (t:Type) ON (t.typeId)");
    await session.RunAsync("CREATE INDEX method_id_index FOR (m:Method) ON (m.methodId)");
    
    Console.Write("][TT");
    await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeTypeDependencies.csv' AS line
MATCH (src:Type), (dst:Type)
WHERE src.typeId = toInteger(line.src) AND dst.typeId = toInteger(line.dst)
CREATE (src)-[:DEPENDS_ON]->(dst)
");

    Console.Write("][TM");
    await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeMethods.csv' AS line
MATCH (src:Type), (dst:Method)
WHERE src.typeId = toInteger(line.src) AND dst.methodId = toInteger(line.dst)
CREATE (src)-[:DECLARES]->(dst)
");

    Console.Write("] ... ");
}
finally
{
    await session.CloseAsync();
    await driver.CloseAsync();
}

CREATE INDEX 查询立即返回。可能是合法的,我不知道 Neo4j 可以多快索引大约 1M 节点中的数字属性。在浏览器中运行:schema 可以确认这两个索引,但我感觉它们不起作用。

运行上述代码需要将近 3 个小时。我做错了什么?

编辑 1

所以我将最后两个查询更改为使用MERGE 子句:

    Console.Write("][TT");
    await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeTypeDependencies.csv' AS line
MERGE (src:Type {typeId: toInteger(line.src)})-[:DEPENDS_ON]->(dst:Type {typeId: toInteger(line.dst)})
");
    
    Console.Write("][TM");
    await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeMethods.csv' AS line
MERGE (src:Type {typeId: toInteger(line.src)})-[:DECLARES]->(dst:Method {methodId: toInteger(line.dst)})
");

现在应该好多了,因为我认为我之前所做的导致节点之间的笛卡尔乘法。然而最后一个查询花费了未知的时间(目前不知道多长时间) - 仍然很糟糕。

【问题讨论】:

  • 索引和约束的创建是异步的,因此您很可能在索引和约束建立之前就抢先一步。您可能应该使用CALL db.awaitIndexes() 以防万一。此外,不推荐使用 MERGE 方法,因为它必须检查这种模式是否存在,如果不存在则创建整个模式,这将导致重复节点。在节点上匹配,然后创建或合并关系,是更好的方法(如果 rel 可能已经存在,则只有 MERGE)。忽略笛卡尔积警告,这正是您创建关系所需要的(每行 1 x 1)。
  • 所以基本上,您是在告诉我回到 EDIT 1 之前的版本。我将添加等待索引。
  • @InverseFalcon - 请安排您的评论作为答案,所以我可以相信您,因为它有效!我猜db.awaitIndexes 是关键。非常感谢。

标签: .net csv neo4j


【解决方案1】:

索引和约束的创建是异步的,因此您很可能会在索引和约束建立之前抢先一步。以防万一,您可能应该使用CALL db.awaitIndexes()

另外,不推荐使用 MERGE 方法,因为它必须检查这样的模式是否存在,如果不存在则创建整个模式,这将导致重复节点。在节点上匹配,然后创建或合并关系,是更好的方法(如果 rel 可能已经存在,或者相同的节点可能在给定输入数据的多行上匹配,则只有 MERGE)。

忽略笛卡尔积警告,这正是您创建关系所需要的(每行 1 x 1)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-13
    • 2013-10-05
    • 1970-01-01
    • 2014-01-03
    • 2019-07-02
    • 1970-01-01
    • 2021-08-07
    相关资源
    最近更新 更多