Neo4j:具有大量节点的慢聚合

时间:2017-02-08 12:34:20

标签: neo4j


我是Neo4j的新手。目前我正在评估Neo4j在我们的一个用例中的使用情况。当我尝试与大量节点聚合时,我遇到了问题。

我们正在使用Neo4j来存储投票与不同类别相关的访问者。投票有一个位置和一个推荐人,我将其建模为单个节点,以便重复使用'它们。
这大致是我的架构的样子(Node Vote有额外的关系,这里省略了):

(Visitor {id})-[VOTED]->(Vote)-[RELATES_TO]->(Category {name})

使用Vistor.id和Category.name

的索引

我们的数据集非常庞大,因此我开始使用400万票和300万访问者测试我的架构。投票涉及18个不同的类别 我希望能够进行聚合。例如,我需要获得投票选出A'类别A'和'类别B'

MATCH (c:Category)<-[:PARENT*0..3]-(child:Category)<-[:RELATES_TO]-(v:Vote)<-[:VOTED]-(visitor:Visitor)
WHERE c.name = 'Category A'
WITH visitor
MATCH (c:Category)<-[:RELATES_TO]-(v:Vote)<-[:VOTED]-(visitor)
WHERE c.name = 'Category B'
RETURN count(distinct(visitor.id)) as Cat_A_and_B_lovers

这需要将近30秒

我还希望能够获得所有类别和不同选票的数量:

MATCH (n:Category)<-[:RELATES_TO]-(:Vote)<-[:VOTED]-(v:Visitor)
RETURN n.name as category, count(DISTINCT v) as count;

这需要13秒。

是否可以改善这些时间,或者Neo4j不适合此类分析?

我们还在分析每个访问者级别的数据 - 但是也希望能够进行这些聚合。

我的设置:
Linux,4核14GB
Noe4j社区版
neo4j.conf:

dbms.memory.heap.initial_size = 2G
dbms.memory.heap.max_size = 10G
dbms.memory.pagecache.size = 10g

- 更新15/02

根据迈克尔的建议,我能够改善两个查询的执行时间。我的第一个问题是:

PROFILE MATCH (n:Category)<-[:RELATES_TO]-()<-[:VOTED]-(v:Visitor)
WITH n, count(DISTINCT v) as count
RETURN n.name as category, count

PROFILE输出:

9964 ms

编译器CYPHER 3.1

Planner COST

运行时解释

+-------------------+----------------+---------+---------+--------------------------------------+------------------------------------+
| Operator          | Estimated Rows | Rows    | DB Hits | Variables                            | Other                              |
+-------------------+----------------+---------+---------+--------------------------------------+------------------------------------+
| +ProduceResults   |           1545 |      18 |       0 | category, count                      | category, count                    |
| |                 +----------------+---------+---------+--------------------------------------+------------------------------------+
| +Projection       |           1545 |      18 |      18 | category -- count, n                 | {category : n.name, count : count} |
| |                 +----------------+---------+---------+--------------------------------------+------------------------------------+
| +EagerAggregation |           1545 |      18 |       0 | count -- n                           | n                                  |
| |                 +----------------+---------+---------+--------------------------------------+------------------------------------+
| +Filter           |        2386269 | 4114784 | 4114784 | anon[19], anon[35], anon[37], n, v   | v:Visitor                          |
| |                 +----------------+---------+---------+--------------------------------------+------------------------------------+
| +Expand(All)      |        2386269 | 4114784 | 8229568 | anon[37], v -- anon[19], anon[35], n | ()<-[:VOTED]-(v)                   |
| |                 +----------------+---------+---------+--------------------------------------+------------------------------------+
| +Expand(All)      |        4113784 | 4114784 | 4114807 | anon[19], anon[35] -- n              | (n)<-[:RELATES_TO]-()              |
| |                 +----------------+---------+---------+--------------------------------------+------------------------------------+
| +NodeByLabelScan  |             23 |      23 |      24 | n                                    | :Category                          |
+-------------------+----------------+---------+---------+--------------------------------------+------------------------------------+

现在需要将近10秒钟。我需要在1秒以下得到它。这是否可行(使用neo4j)?

1 个答案:

答案 0 :(得分:0)

请勿配置比您更多的内存。

将页面缓存设置为4G,将min / max-heap设置为8

对于查询,最好使用EXPLAINPROFILE查看查询计划。

我推荐这样的东西:

MATCH (ca:Category)<-[:PARENT*0..3]-(child)<-[:RELATES_TO]-()<-[:VOTED]-(visitor)
WHERE ca.name = 'Category A'
WITH distinct visitor
MATCH (cb:Category)<-[:RELATES_TO]-()<-[:VOTED]-(visitor)
USING INDEX cb:Category(name)
WHERE cb.name = 'Category B'
RETURN count(distinct(visitor)) as Cat_A_and_B_lovers

您的原始查询每个类别有一个访问者重复并投票,因此可能会有很多重复项,因为每个重复项都会执行第二次匹配。

每个类别树A有多少访问者? 你也可以翻转两个,即先做'B'然后'A'。

MATCH (cb:Category)<-[:RELATES_TO]-()<-[:VOTED]-(visitor)
WHERE cb.name = 'Category B'
WITH distinct visitor
MATCH (ca:Category)<-[:PARENT*0..3]-(child)<-[:RELATES_TO]-()<-[:VOTED]-(visitor)
USING INDEX ca:Category(name)
WHERE ca.name = 'Category A'
RETURN count(distinct(visitor)) as Cat_A_and_B_lovers

同样,对于计数而言,当节点本身代表相同的概念时,计算属性(visitor.id)只是代价高昂且无益。