【问题标题】:Scaling graph traversals in ArangoDb在 ArangoDb 中缩放图遍历
【发布时间】:2017-12-10 23:23:50
【问题描述】:

我有一个树状结构,它实际上是一个有向无环图。一个小版本如下所示。

在任何父母,我想总结子树的一些属性。今天我在 AQL 中使用基于起始节点的 TRAVERSAL 和 COLLECT:

for c in traversal(nodes,nodeTree,ch,'inbound',{uniqueness:{vertices:'global'}})
    collect child = ch._id into group

然后我可以在组上使用聚合。 (对于 ArangoDB 2.8,我相信现在可以直接在 collect 语句中完成。)唯一性选项解决了重复问题。

缩放

当树(图)增长到相当大的规模(10-20k 个节点)时,这将如何扩展?我需要它快速,因为用户将等待响应(不是长时间运行的工作)。

我正在考虑在节点中缓存值并使用 dirty 标志。然后在节点 1 中,如果它们都是 clean,则可以将 2 和 3 相加。 问题是 5 包含在 2 和 3 的总和中。

我该如何解决这个问题?或者这不是问题——遍历真的那么快吗?

到目前为止,我已经想出了让每个节点都包含其子树重复项的列表的想法,在 1 的情况下,这意味着信息“5 被包含两次”。这可以用来从 1 的总数中减去它。但是我如何找到这些信息?我考虑过找到所有具有>1个父节点的节点,然后向上遍历(很快),然后以某种方式计算此信息。

【问题讨论】:

    标签: graph graph-databases arangodb


    【解决方案1】:

    遍历的运行时间受限于过程中实际接触到的顶点和边的数量。因此,遍历的运行时间取决于路径的深度和分支因子(预期有多少具有多个父节点的顶点)。

    您描述的构造的问题是遍历将选择从15 的一条路径(比如左侧)并将所有值相加并最终返回到1 以选择正确的路径。现在它再次到达5,但这次搜索深度低于上次看到5,因此它必须再次实际遍历5 上的子树,因为它现在可能在这条路径上获得更大的距离(它不知道这个子树上的所有顶点都可以在更短的距离内到达)。此路径上的顶点不会再次调用访问者,但仍会被遍历和跟踪,这会花费时间。

    我尝试优化遍历以验证缩放。 首先我注册了一个新的优化访问者:

    require("@arangodb/aql/functions").register("test::counter", "function (config, result, vertex) {result[0] = result[0] || {value: 0}; result[0].value += vertex.value}");
    

    这个访问者对顶点的值求和并直接返回它们,所以我可以摆脱COLLECT 语句。我可以在我的 AQL 中使用它:

    FOR x IN TRAVERSAL(TestVertices, TestEdges, 'TestVertices/0', 'outbound', {uniqueness:{vertices:'global'}, visitor: 'test::counter', maxDepth: 5012})
      RETURN x.value
    

    这里注意:我在选项中给出了maxDepth,以实际进行高深度搜索,默认为256

    我的测试树基本上是一个20.000 顶点链,其中每三个顶点在链的后面有一个随机顶点的附加边(模拟你描述的多父问题)

    通过这种遍历,我设法从~5 secs 的根中搜索了5012 的深度。使用更高的深度,它会呈指数增长。

    我假设您的图表具有较少的多个父项,因此我希望您的图表上的运行时间更少。

    如果您预计读取次数多于写入次数,您还可以考虑计算每次写入的总和。 这会减慢写入速度,但会立即进行所有读取。

    例如,您可以在更新值时使用以下 AQL:

    LET i = (FOR x IN 1..5012 INBOUND @start TestEdges
               RETURN DISTINCT x) 
      FOR x IN i UPDATE x WITH {sum: x.sum + @add} IN TestVertices
    

    使用绑定参数@add 表示要添加的值,@start 表示要更新的顶点。使用这种技术,您的读取查询很简单:

    FOR x IN TestVertices FILTER x._id == @start 
      RETURN x.sum
    

    希望这会有所帮助。

    【讨论】:

    • 感谢您的全面回答。似乎遍历可以相当快。在我的情况下,图表很可能是宽而不是深。 MaxDepth 50 我会说。
    • 我绝对期望读取比写入更多,这就是为什么我正在考虑缓存总和,就像你举一个例子一样。我在这里担心的是 1)“热”根(每次写入都访问 - 块等)和 2)确信总和保持正确(交易可能会对此有所帮助)
    • 2) 为了正确,您可以在 foxx 应用程序中为多个 AQL 查询实现事务 (docs.arangodb.com/Foxx/index.html) 1) 是的,您是对的,“热”根可能会阻塞,但我实际上会给出尝试查看您期望的模拟数据(甚至真实数据)的性能,并检查它是否真的是一个问题。对于用户感知的“非阻塞”,写入可能足够快,因为实际读取始终是一个恒定的时间动作。因此,与阻塞根相比,用户的平均等待时间可能会更低。
    • 这对于 ArangoDB v3 会有什么影响?现在不再有访问者函数(使用新的遍历语法)?
    • 看来collect aggregate 来救援就好了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多