go match fetch lookup 查询效率对比

如过某个查询逻辑,以上语言都可实现,那么查询速度谁要快一点?

go/lookup/fetch 或者它们的组合可能可以被用 match 表达(但是 go 的 点探索和 match 其实不等价,参考文档里对 walk 的介绍)

一般来说 match 的性能小于等于非match

参考

3 个赞

@wey 的这两篇文章讲得很详细,基本上可以覆盖平时可能遇到的很多场景。刚好最近有在做一些语句优化的内容,抛砖引玉提一些比较小但可能很重要的点。

首先结论是: 对于一些简单场景,match 的性能确实相对会比较差,比如说单纯的 lookup tag 或者简单的多跳数拓展。但是在一些特定的复杂场景下,match 的性能可能更好,举个栗子:

MATCH (a:A)-[:follow]->(b:B)-[*1..3]->(c:A) where id(a)=="a1"
MATCH (e:E)--(b)-->(d:D)
RETURN d

我们假设数据中 (:A)-[:follow]->(:B)(:B)-->(:D) 的平均出度数比较高,而其他 pattern,比如 (:E)-->(b)(:B)-[*1..3]->(:A) ,涉及的平均度数都比较低并且 E 和 A 的实体数量比较少。
一种 go 语句的写法类似: (a)-[:follow]->{ { (b)-->(c) }-->(d) }--(e),如 @wey 所说,这里涉及到 go 和 match 路径模式的不同导致语义并不完全等价,但路径模式的本质是 path predicate,比如 TAIL 是对路径上的边去重复,通过改写 go 语句加入 where 条件保证一次拓展中边的类型、起点、终点和 rank 都不相同,还是有可能实现等价语义的。但在一些不能被等价改写的情况下(比如多步拓展 (b:B)-[*1..3]->(c:A)),路径模式对性能的影响也是显而易见的,这种情况下 match 的性能有可能会好很多。

当然,该场景下上述 pattern 计算顺序对 go 和 match 来说都不是最优解,因为“(:E)-->(b)(:B)-[*1..3]->(:A) 涉及的平均度数都比较高并且 E 和 A 的实体数量比较少”这种数据特征的存在,我们可以做更多的优化来保证计算过程中尽可能早尽可能多地剪枝。
这些数据特征可以从统计信息获取并有可能被 CBO optimizer 利用从而自动优化执行计划。nebula 目前还不支持 CBO,但从执行计划上来看,match 比 go 有更好的可优化性。但是在已知这些数据特征的情况下,我们仍然可以通过改写 match 语句来优化性能,而 lookup/go 却很难改写,原因是 lookup/go 对于变量传递的限制,lookup 的 yield 子句并不能传递管道前变量,而 go 必须在 from 子句中引用才能在 yield 子句中传递管道前变量。lookup/go 最好的改写可能是 (e)-->{ { (b)-->(d) }-->(a) }--(c)。不能自由的传递变量可能会造成额外的计算量,而这可能带来无法接受的开销。

总结一下:

  1. go 语句 WALK 路径模式带来的额外计算可能会对性能有较大影响
  2. match 语句中可以更自由传递和引用变量的特性可能对较早的数据剪枝更友好
  3. 通过手动改写语句方式优化 match 语句是可行的,但 lookup/go 却限制很多
  4. 从 optimizer 的视角来看,match 语句的执行计划有更高的可优化性和更好的优化程度
6 个赞

学习了:+1:

此话题已在最后回复的 30 天后被自动关闭。不再允许新回复。