我是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)?
答案 0 :(得分:0)
请勿配置比您更多的内存。
将页面缓存设置为4G,将min / max-heap设置为8
对于查询,最好使用EXPLAIN
和PROFILE
查看查询计划。
我推荐这样的东西:
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)只是代价高昂且无益。