NebulaGraph 的数据分布方式是其高性能、高可用性设计的核心基础,主要围绕 分片(Partition)、副本(Replica) 和 Raft 协议 实现,同时结合数据分割策略和元信息管理确保数据的均衡分布与可靠存储。以下是详细解析:
NebulaGraph 的存储层(storaged)采用 分片式存储,将数据划分为多个独立的 分片(Partition),每个分片通过 副本(Replica) 机制保证高可用。
- 定义:分片是数据存储的最小逻辑单位,类似于关系型数据库中的 “分区表”,但粒度更细且分布式存储。
- 数量:创建图空间(Graph Space)时需指定分片数量(
partition_num),默认值为 100,用户可根据数据量调整(建议设置为集群存储节点数的整数倍,便于均衡分布)。
- 数据映射:每个节点(Vertex)和边(Edge)会通过哈希算法映射到特定分片。具体规则是:
- 节点根据 ID 的哈希值 映射到分片(
hash(vertex_id) % partition_num)。
- 边根据 起点 ID 的哈希值 映射到分片(边分割策略,见下文),即边与起点存储在同一个分片,避免跨分片查询。
- 定义:每个分片会复制为多个副本,组成一个 Raft Group(分布式一致性协议组),通过 Raft 协议实现副本间的数据同步和 leader 选举。
- 数量:创建图空间时需指定副本数量(
replica_factor),默认值为 3,副本会分散在不同的存储节点(反亲和性部署),避免单点故障。
- 角色:每个 Raft Group 中存在一个 Leader 副本 和多个 Follower 副本:
- Leader 副本:处理所有读写请求,确保数据一致性;
- Follower 副本:同步 Leader 的数据,Leader 故障时参与选举新 Leader。
NebulaGraph 采用 边分割(Edge-Cut) 策略存储节点和边,这是图数据库中常见的分布式存储方案,区别于 “点分割(Vertex-Cut)”。
- 节点存储:每个节点只属于一个分片(根据节点 ID 映射),所有副本中仅存储一份节点属性数据(由 Raft 保证一致性)。
- 边存储:边会跟随其 起点(Source Vertex) 存储在同一个分片。例如,边
(u) -> (v) 会存储在 u 所在的分片,而 (v) -> (u) 会存储在 v 所在的分片(若存在反向边)。
- 优势:查询节点的出边时(如
GO FROM u OVER e),可直接访问 u 所在分片的 Leader 副本,避免跨分片数据拉取,效率极高。
- 局限:若需查询节点的入边(如
GO FROM v REVERSELY OVER e),可能需要跨分片查询(因为入边存储在起点所在分片),此时需通过元信息定位目标分片。
- 集群的元信息(如分片数量、副本分布、节点状态等)由 Metad 服务 维护,所有 Graphd 和 Storaged 节点通过 Metad 同步元信息。
- 当查询请求到达 Graphd 时,Graphd 会先向 Metad 查询目标节点 / 边所在的分片及对应 Leader 副本的位置,再直接访问该 Storaged 节点。
- 分片的副本会均匀分布在集群的存储节点上,由 Metad 根据节点负载和可用性动态调整(如节点故障时迁移副本)。
- 例如,若集群有 3 个存储节点、副本数为 3,则每个分片的 3 个副本会分别部署在 3 个节点上,确保单节点故障不影响数据可用性。
- 随着数据量增长或节点扩缩容,Metad 支持 分片迁移(通过 Raft 协议同步数据后切换 Leader),确保各存储节点的负载均衡。
- 迁移过程中,查询仍可正常执行(由 Raft 保证数据一致性),无感知中断。
- 分片式存储:数据按节点 / 边 ID 哈希映射到固定分片,确保数据均匀分布。
- 边分割策略:边跟随起点存储,优化出边查询效率,减少跨分片开销。
- 多副本与 Raft:每个分片的多副本通过 Raft 协议保证一致性,Leader 副本处理读写,Follower 提供冗余。
- 元信息驱动:Metad 统一管理分片分布和副本状态,Graphd 基于元信息精准访问目标存储节点。
这种分布方式使得 NebulaGraph 能够支持海量图数据的分布式存储,并在高并发查询场景下保持高效性能。