提高 Neo4j 查询性能
Improve Neo4j query performance
我有一个搜索多个实体的 Neo4j 查询,我想使用节点对象批量传递参数。但是,我的查询执行速度不是很高。如何优化此查询并使其性能更好?
WITH $nodes as nodes
UNWIND nodes AS node
with node.id AS id, node.lon AS lon, node.lat AS lat
MATCH
(m:Member)-[mtg_r:MT_TO_MEMBER]->(mt:MemberTopics)-[mtt_r:MT_TO_TOPIC]->(t:Topic),
(t1:Topic)-[tt_r:GT_TO_TOPIC]->(gt:GroupTopics)-[tg_r:GT_TO_GROUP]->(g:Group)-[h_r:HAS]->
(e:Event)-[a_r:AT]->(v:Venue)
WHERE mt.topic_id = gt.topic_id AND
distance(point({ longitude: lon, latitude: lat}),point({ longitude: v.lon, latitude: v.lat })) < 4000 AND
mt.member_id = id
RETURN
distinct id as member_id,
lat as member_lat,
lon as member_lon,
g.group_name as group_name,
e.event_name as event_name,
v.venue_name as venue_name,
v.lat as venue_lat,
v.lon as venue_lon,
distance(point({ longitude: lon,
latitude: lat}),point({ longitude: v.lon, latitude: v.lat })) as distance
查询分析如下所示:
因此,您当前的计划有 3 个并行线程。我们暂时可以忽略一个,因为它的命中率为 0db。
您受到的最大打击是 (mt:MemberTopics) ... WHERE mt.member_id = id
的比赛。我猜 member_id 是一个唯一的 ID,因此您需要在其上创建一个索引 CREATE INDEX ON :MemberTopics(member_id)
。这将允许 Cypher 进行索引查找而不是节点扫描,这会将数据库命中率从 ~30mill 减少到~1(此外,在某些情况下,内联 属性 匹配对于更复杂的查询更快。所以(mt:MemberTopics {member_id:id})
比较好。它明确说明匹配时这个条件必须永远为真,并且会加强使用索引查找)
第二大打击是点距检查。现在,这是独立完成的,因为节点扫描需要很长时间。一旦你对 MemberTopic 进行了更改,规划器应该切换到查找所有连接的场地,然后只对 thous 进行距离检查,这样也应该变得更便宜。
此外,看起来 mt 和 gt 是由主题 link 编辑的,并且您正在使用主题 ID 来对齐它们。如果假设 t 和 t1 是同一个主题节点,您可以只对两个节点使用 t 来强制执行它,然后您不需要对 link mt 和 gt 进行 id 检查。如果 t 和 t1 不是同一个节点,在节点的属性中使用外键是一个标志,表明您应该在两个节点之间建立关系,并且只沿着该边缘移动(关系也可以有属性,但上下文看起来很像 t 和 t1 应该是同一个节点。您也可以通过说 WHERE t = t1
来强制执行此操作,但此时,您应该只对两个节点使用 t)
最后,根据查询的行数 return,您可能希望使用 LIMIT 和 SKIP 对结果进行分页。这看起来像是发给用户的信息,我怀疑他们是否需要完整转储。所以只有 return 最上面的结果,只有在用户想看到更多的时候才处理其余的。 (当结果接近一公吨时很有用)由于到目前为止您只有 21 个结果,所以这现在不是问题,但请记住,因为您需要扩展到 100,000 个以上的结果。
我有一个搜索多个实体的 Neo4j 查询,我想使用节点对象批量传递参数。但是,我的查询执行速度不是很高。如何优化此查询并使其性能更好?
WITH $nodes as nodes
UNWIND nodes AS node
with node.id AS id, node.lon AS lon, node.lat AS lat
MATCH
(m:Member)-[mtg_r:MT_TO_MEMBER]->(mt:MemberTopics)-[mtt_r:MT_TO_TOPIC]->(t:Topic),
(t1:Topic)-[tt_r:GT_TO_TOPIC]->(gt:GroupTopics)-[tg_r:GT_TO_GROUP]->(g:Group)-[h_r:HAS]->
(e:Event)-[a_r:AT]->(v:Venue)
WHERE mt.topic_id = gt.topic_id AND
distance(point({ longitude: lon, latitude: lat}),point({ longitude: v.lon, latitude: v.lat })) < 4000 AND
mt.member_id = id
RETURN
distinct id as member_id,
lat as member_lat,
lon as member_lon,
g.group_name as group_name,
e.event_name as event_name,
v.venue_name as venue_name,
v.lat as venue_lat,
v.lon as venue_lon,
distance(point({ longitude: lon,
latitude: lat}),point({ longitude: v.lon, latitude: v.lat })) as distance
查询分析如下所示:
因此,您当前的计划有 3 个并行线程。我们暂时可以忽略一个,因为它的命中率为 0db。
您受到的最大打击是 (mt:MemberTopics) ... WHERE mt.member_id = id
的比赛。我猜 member_id 是一个唯一的 ID,因此您需要在其上创建一个索引 CREATE INDEX ON :MemberTopics(member_id)
。这将允许 Cypher 进行索引查找而不是节点扫描,这会将数据库命中率从 ~30mill 减少到~1(此外,在某些情况下,内联 属性 匹配对于更复杂的查询更快。所以(mt:MemberTopics {member_id:id})
比较好。它明确说明匹配时这个条件必须永远为真,并且会加强使用索引查找)
第二大打击是点距检查。现在,这是独立完成的,因为节点扫描需要很长时间。一旦你对 MemberTopic 进行了更改,规划器应该切换到查找所有连接的场地,然后只对 thous 进行距离检查,这样也应该变得更便宜。
此外,看起来 mt 和 gt 是由主题 link 编辑的,并且您正在使用主题 ID 来对齐它们。如果假设 t 和 t1 是同一个主题节点,您可以只对两个节点使用 t 来强制执行它,然后您不需要对 link mt 和 gt 进行 id 检查。如果 t 和 t1 不是同一个节点,在节点的属性中使用外键是一个标志,表明您应该在两个节点之间建立关系,并且只沿着该边缘移动(关系也可以有属性,但上下文看起来很像 t 和 t1 应该是同一个节点。您也可以通过说 WHERE t = t1
来强制执行此操作,但此时,您应该只对两个节点使用 t)
最后,根据查询的行数 return,您可能希望使用 LIMIT 和 SKIP 对结果进行分页。这看起来像是发给用户的信息,我怀疑他们是否需要完整转储。所以只有 return 最上面的结果,只有在用户想看到更多的时候才处理其余的。 (当结果接近一公吨时很有用)由于到目前为止您只有 21 个结果,所以这现在不是问题,但请记住,因为您需要扩展到 100,000 个以上的结果。