索引查询恶化查询性能
Index queries worsen query performance
我试图在我的一个查询上使用索引以使其 运行 更快,但它使另一个查询 运行 变慢。所以我有一个数据处理查询?其中 运行 使用索引更快,另一个用于从 Neo4j(使用 Kafka)生成数据并开始使用索引 运行 变慢。我在数据处理之前创建索引,并在需要生成数据时删除它,但这不是一种有效的技术。 Neo4j 如何使用索引实际查询 运行 变慢?
这里是数据制作的查询:
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: m.lon, latitude: m.lat}),point({ longitude: v.lon, latitude: v.lat })) < 4000
RETURN distinct mt.member_id as member_id, m.lat as member_lat, m.lon as member_lon
没有索引的查询性能:
6432058 次 1888 毫秒内的数据库总命中数
带索引的查询的分析计划:
149617 毫秒内总数据库命中数为 138425061
索引查询如下所示:
CREATE INDEX ON:MemberTopics(member_id)
CREATE INDEX ON:MemberTopics(topic_id)
CREATE INDEX ON:GroupTopics(topic_id)
索引查询性能较差的原因是 Cypher 规划器错误地计算了它承诺的工作量(估计行数与实际行数)。
在没有索引的情况下,规划器知道它必须对 MemberTopics 进行标签扫描,并且内部统计表明这将达到约 300 万行(这是正确的)。有了 Membertopics 上的索引,规划器的内部统计表明,如果使用该索引,它可能需要 ~2k 行,而实际结果是 ~7000 万行……哎呀!这部分是因为规划器低估了在查询的此时要处理的行数,但我不能确定为什么它会偏离这么多数量级。如果您从密码中删除成员节点并将 return 更改为 RETURN *
,看起来您会得到该问题的答案。
这部分是因为您的 cypher/data 本身就是一个需要计划的怪物。
- 您匹配了 2 个未在别处使用的主题节点(t 和 t1)
WHERE mt.topic_id = gt.topic_id
是一个 foreign key 引用,永远不应该出现在密码中;这真的是你表现糟糕的关键。 topic_id 应该是它自己的节点,或者你应该在 mt 和 gt 之间有直接关系。 Neo4j 非常擅长走关系边缘。外键很糟糕(与处理关系相比)。
- 你有 2 条长路径,没有明确说明它们之间的关系(从 Cypher 规划者的角度来看)。
- 你的 MATCH 是一对没有明确起点的长路径链。不将其中一个节点锁定到特定(少数)节点的长路径几乎肯定会激增您的查询行数,从而激增您的性能。
至于你能做些什么,
- 限制查询范围
对单个成员、组或区域
- 使用 WITH
将您的查询分成多个阶段
With 创建了 Cypher 的逻辑分区,规划器将主要尝试解决 WITH 之前的所有问题,然后再继续其余的逻辑。您比计划员更了解您的数据,因此这是在不更改结果的情况下限制查询范围的好方法。 (在你的情况下,可能只从 Member 和 Venue 开始,然后从那里过滤。)
- 不要使用外键
用关系或中间节点替换mt.topic_id = gt.topic_id
。
我试图在我的一个查询上使用索引以使其 运行 更快,但它使另一个查询 运行 变慢。所以我有一个数据处理查询?其中 运行 使用索引更快,另一个用于从 Neo4j(使用 Kafka)生成数据并开始使用索引 运行 变慢。我在数据处理之前创建索引,并在需要生成数据时删除它,但这不是一种有效的技术。 Neo4j 如何使用索引实际查询 运行 变慢?
这里是数据制作的查询:
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: m.lon, latitude: m.lat}),point({ longitude: v.lon, latitude: v.lat })) < 4000
RETURN distinct mt.member_id as member_id, m.lat as member_lat, m.lon as member_lon
没有索引的查询性能:
6432058 次 1888 毫秒内的数据库总命中数
带索引的查询的分析计划:
149617 毫秒内总数据库命中数为 138425061
索引查询如下所示:
CREATE INDEX ON:MemberTopics(member_id)
CREATE INDEX ON:MemberTopics(topic_id)
CREATE INDEX ON:GroupTopics(topic_id)
索引查询性能较差的原因是 Cypher 规划器错误地计算了它承诺的工作量(估计行数与实际行数)。
在没有索引的情况下,规划器知道它必须对 MemberTopics 进行标签扫描,并且内部统计表明这将达到约 300 万行(这是正确的)。有了 Membertopics 上的索引,规划器的内部统计表明,如果使用该索引,它可能需要 ~2k 行,而实际结果是 ~7000 万行……哎呀!这部分是因为规划器低估了在查询的此时要处理的行数,但我不能确定为什么它会偏离这么多数量级。如果您从密码中删除成员节点并将 return 更改为 RETURN *
,看起来您会得到该问题的答案。
这部分是因为您的 cypher/data 本身就是一个需要计划的怪物。
- 您匹配了 2 个未在别处使用的主题节点(t 和 t1)
WHERE mt.topic_id = gt.topic_id
是一个 foreign key 引用,永远不应该出现在密码中;这真的是你表现糟糕的关键。 topic_id 应该是它自己的节点,或者你应该在 mt 和 gt 之间有直接关系。 Neo4j 非常擅长走关系边缘。外键很糟糕(与处理关系相比)。- 你有 2 条长路径,没有明确说明它们之间的关系(从 Cypher 规划者的角度来看)。
- 你的 MATCH 是一对没有明确起点的长路径链。不将其中一个节点锁定到特定(少数)节点的长路径几乎肯定会激增您的查询行数,从而激增您的性能。
至于你能做些什么,
- 限制查询范围
对单个成员、组或区域
- 使用 WITH 将您的查询分成多个阶段
With 创建了 Cypher 的逻辑分区,规划器将主要尝试解决 WITH 之前的所有问题,然后再继续其余的逻辑。您比计划员更了解您的数据,因此这是在不更改结果的情况下限制查询范围的好方法。 (在你的情况下,可能只从 Member 和 Venue 开始,然后从那里过滤。)
- 不要使用外键
用关系或中间节点替换mt.topic_id = gt.topic_id
。