图分区
当 JanusGraph 部署在多个存储后端实例的集群上时,图会在这些机器上进行分区。由于 JanusGraph 以邻接列表表示法存储图,因此顶点到机器的分配决定了分区。默认情况下,JanusGraph 使用随机分区策略,将顶点随机分配给机器。随机分区非常高效,无需配置,并能产生均衡的分区。目前不支持显式分区。
cluster.max-partitions = 32
ids.placement = simple
配置选项 max-partitions 控制 JanusGraph 创建的虚拟分区数量。这个数字应该大约是存储后端实例数量的两倍。如果存储后端实例集群预计会增长,请估算可预见的未来集群规模,并以此数字作为基线。将此数字设置得过大将不必要地碎片化集群,这可能导致性能下降。这个数字应该大于 JanusGraph 图中预期的最大节点数量。它必须大于 1 且为 2 的幂。
图分区有两个方面可以单独控制:边切割和顶点切割。
边切割
在将顶点分配到分区时,人们力求优化分配,使得频繁共同遍历的顶点托管在同一台机器上。假设顶点 A 分配给机器 1,顶点 B 分配给机器 2。顶点之间的边被称为切割边,因为其端点托管在不同的机器上。作为图查询的一部分遍历此边需要机器之间的通信,这会减慢查询处理速度。因此,减少频繁遍历边的边切割是可取的。反过来,这需要将频繁遍历边的相邻顶点放置在同一分区中。
顶点通过分配的顶点 ID 放置在分区中。分区本质上是顶点 ID 的一个连续范围。为了将顶点放置在特定分区中,JanusGraph 从该分区的顶点 ID 范围中选择一个 ID。JanusGraph 通过配置的放置策略控制顶点到分区的分配。默认情况下,在同一事务中创建的顶点被分配到同一分区。这种策略易于理解,并且在频繁共同遍历的顶点在同一事务中创建的情况下(通过为此目的优化加载策略,或因为顶点自然以这种方式添加到图中)效果很好。然而,该策略是有限的,当数据在大型事务中加载时会导致分区不平衡,并且对于许多用例而言不是最佳策略。用户可以通过实现 IDPlacementStrategy 接口并通过 ids.placement 选项在配置中注册来提供特定于用例的顶点放置策略。
在实现 IDPlacementStrategy 时,请注意分区由一个整数 ID 标识,其范围从 0 到配置的虚拟分区数量减 1。对于我们的示例配置,有分区 0, 1, 2, 3, ...31。分区 ID 与顶点 ID 不同。当 JanusGraph 服务器与存储后端位于同一主机上时,边切割更有意义。如果每次遍历的跳跃都必须进行网络调用到不同的主机,则边切割和自定义放置策略的优势可能会在很大程度上被抵消。
顶点切割
虽然边切割优化旨在减少交叉通信,从而提高查询执行效率,但顶点切割解决的是由具有大量入射边的顶点引起的“热点”问题。虽然以顶点为中心的索引有效地解决了大度顶点的查询性能问题,但对于超大型图,仍然需要顶点切割来解决热点问题。
切割顶点意味着将该顶点的邻接列表的一个子集存储在图中的每个分区上。换句话说,顶点及其邻接列表被分区,从而有效地将单个顶点的负载分散到集群中的所有实例,并消除热点。
JanusGraph 按标签切割顶点。可以将顶点标签定义为“已分区”,这意味着该标签的所有顶点将按照上述方式在集群中进行分区。
mgmt = graph.openManagement()
mgmt.makeVertexLabel('user').make()
mgmt.makeVertexLabel('product').partition().make()
mgmt.commit()
在上面的例子中,`product` 被定义为分区顶点标签,而 `user` 是普通标签。这种配置适用于有数千种产品但有数百万用户,并且记录用户和产品之间交易的情况。在这种情况下,产品顶点将具有非常高的度数,如果它们不被分区,热门产品将成为热点。然而,度数不高的产品也会被分区,导致不必要的开销。
警告
顶点分区功能开销高,尚未达到生产可用状态。我们通常不鼓励使用此功能。更多详情,请参见 讨论#2717。
图分区常见问题
随机分区 vs. 显式分区
当图较小或由少数存储实例容纳时,最好使用随机分区以其简单性。通常,当图增长到数百亿条边时,应强烈考虑启用显式图分区并配置合适的分区启发式算法。