当前位置: 首页 > 图灵资讯 > 技术篇> Nebula Graph开源分布式图数据库,万亿级数据,毫秒级延时

Nebula Graph开源分布式图数据库,万亿级数据,毫秒级延时

来源:图灵教育
时间:2023-11-05 17:34:26

推荐分布式数据库Nebula Graph,万亿数据,毫秒延迟什么是Nebula? Graph

Nebula Graph 它是一个开源、分布式、易于扩展的原始图形数据库,可以携带数千亿点和数万亿条边的超大规模数据集,并提供毫秒查询

file.png

图形数据库是什么?

图形数据库是一个数据库,专门存储庞大的图形网络并从中检索信息。它可以有效地存储图中的数据(Vertex)和边(Edge),属性也可以使用(Property)附加到点和边缘

file.png

图形数据库适用于存储大多数从现实中抽象出来的数据类型。世界上几乎所有领域的事物都有内部联系。建模系统,如关系数据库,将提取实体之间的关系,并将关系单独存储在表和列中,而实体的类型和属性存储在其他列甚至其他表中,这使得数据管理费时费力。

Nebula Graph 作为一个典型的图形数据库,丰富的关系可以通过边缘及其类型和属性自然呈现。

Nebula Graph 的优势开源

Nebula Graph 是在 Apache 2.0 在条款下开发。数据库开发人员、数据科学家、安全专家、算法工程师等越来越多的人参与其中 Nebula Graph 欢迎来到设计和开发中 Nebula Graph GitHub 主页参与开源项目。

高性能

根据图数据库的特点使用 C++ 编写的 Nebula Graph,毫秒级查询可以提供。在许多数据库中,Nebula Graph 在图数据服务领域表现出色,数据规模越大,Nebula Graph 优势越大。详情请参考 Nebula Graph benchmarking 页面。

易扩展

Nebula Graph 采用 shared-nothing 在不停止数据库服务的情况下,架构支持扩大容量。

易开发

Nebula Graph 提供 Java、Python、C++ 和 Go 更多的客户端仍在开发流行编程语言的客户端。详情请参见 Nebula Graph clients。

高可靠访问控制

Nebula Graph 支持严格的角色访问控制和 LDAP(Lightweight Directory Access Protocol)外部认证服务可以有效提高数据安全性。详见验证和授权。

生态多样化

Nebula Graph 越来越多的原生工具开放,比如 Nebula Graph Studio、Nebula Console、Nebula Exchange 等等,更多的工具可以查看生态工具概述。

此外,Nebula Graph 还具备与 Spark、Flink、HBase 在这个充满挑战和机遇的时代,产品整合的能力大大提高了自身的竞争力。

兼容 openCypher 查询语言

Nebula Graph 查询语言,简称 nGQL,是一种声明,部分兼容 openCypher 文本查询语言易于理解和使用。详见语法 nGQL 指南。

面对未来的硬件,读写平衡

闪存设备性能极高,价格迅速下降, Nebula Graph 是一个面向 SSD 与基于设计的产品相比,设计的产品 HDD + 大内存产品更适合未来的硬件趋势,更容易实现读写平衡。

数据建模灵活

用户可以很容易地在那里 Nebula Graph 在没有强制将数据转换为关系表的情况下建立数据模型。并且可以自由添加、更新和删除属性。详情请参考数据模型。

广受欢迎

腾讯、美团、京东、快手、360 等待科技巨头的使用 Nebula Graph。详情请参见 Nebula Graph 官网。

适用场景

Nebula Graph 可用于基于图纸的各种业务场景。为节省将各种数据转换到关系数据库的时间,避免复杂查询,建议使用 Nebula Graph。

欺诈检测

金融机构必须仔细研究大量的交易信息,以检测潜在的金融欺诈行为,并了解欺诈行为与设备之间的内部关系。这个场景可以通过图片建模,然后在帮助下建模 Nebula Graph,欺诈团伙或其他复杂的欺诈行为可以很容易地检测到。

实时推荐

Nebula Graph 能够及时处理访问者产生的实时信息,并准确推送文章、视频、产品和服务。

知识图谱

自然语言可以转化为知识图谱,存储在 Nebula Graph 中间。通过智能问答系统中的语义解析器,可以分析和重新组织使用自然语言组织的问题,然后从知识图中检索问题的可能答案,并提供给提问者。

社交网络

人际信息是典型的图形数据,Nebula Graph 它可以很容易地处理数十亿人和数万亿人之间的社交网络信息,并在大量并发的情况下提供快速的朋友推荐和工作查询。

服务架构总结

Nebula Graph 由三种服务组成:Graph 服务、Meta 服务和 Storage 服务是一种存储和计算分离的架构。

每个服务都有可执行的二进制文件和相应的过程,用户可以在一台或多台计算机上部署这些二进制文件 Nebula Graph 集群。

下图展示了 Nebula Graph 经典的集群架构。

file.png

Meta 服务

在 Nebula Graph 架构中,Meta 服务是由 nebula-metad 提供流程,负责数据管理,如 Schema 操作、集群管理、用户权限管理等。

Graph 服务和 Storage 服务

Nebula Graph 采用计算存储分离架构。Graph 服务负责计算请求的处理,Storage 负责存储数据的服务。它们由不同的过程提供,Graph 服务是由 nebula-graphd 进程提供,Storage 服务是由 nebula-storaged 提供流程。计算存储分离架构的优点如下:

易扩展

分布式体系结构得到保证 Graph 服务和 Storage 服务灵活,扩容缩容方便。

高可用

如果提供 Graph 部分服务器出现故障,其他服务器可以继续为客户提供服务 Storage 服务存储的数据不会丢失。服务恢复速度快,甚至可以让用户无意识。

节约成本

存储分离架构的计算可以提高资源利用率,并根据业务需要灵活控制成本。

更多可能性

基于分离架构的特点,Graph 在更多类型的存储引擎上,服务可以单独运行,Storage 服务还可以为各种目的的计算引擎提供服务。

Meta 为Meta服务 服务架构

file.png

Meta 服务是由 nebula-metad 用户可以根据场景配置提供流程 nebula-metad 进程数量:

  • 用户可以在测试环境中 Nebula Graph 集群中部署 1 个或 3 个 nebula-metad 过程。如果要部署的话 3 用户可以部署它们 1 机器上,或分别部署在不同的机器上。
  • 建议在生产环境中 Nebula Graph 集群中部署 3 个 nebula-metad 过程。请在不同的机器上部署这些过程,以确保高可用性。

所有 nebula-metad 过程构成了基础 Raft 协议集群的过程之一是 leader,所有其他过程都是 follower。

leader 它是由大多数派选出的,只有 leader 能够为客户端或其它组件提供服务,其它组件 follower 如果作为候补 leader 一切都会出现故障 follower 选举新的 leader。

leader 和 follower 的数据通过 Raft 因此,协议保持一致 leader 新的故障和选举 leader 不会导致数据不一致。更多关于 Raft 的介绍见 Storage 服务

Meta 服务功能管理用户账户账户

Meta 用户的账户和权限信息存储在服务中,当客户端通过账户发送请求时 Meta 服务,Meta 服务将检查账户信息,以及账户是否有相应的请求权限。

更多 Nebula Graph 访问控制说明书,请参见身份验证。

管理分片

Meta 该服务负责存储和管理分片的位置信息,并确保分片的负载平衡。

管理图空间

Nebula Graph 支持多个图形空间,安全隔离不同图形空间中的数据。Meta 存储所有图空间的元数据(非完整数据),并跟踪数据的变化,如增加或删除图空间。

管理 Schema 信息

Nebula Graph 它是一个强类型图数据库 Schema 包括 Tag、Edge type、Tag 属性和 Edge type 属性。

Meta 存储在服务中 Schema 同时,信息也负责 Schema 添加、修改和删除,并记录其版本。

更多 Nebula Graph 的 Schema 信息,请参见数据模型。

管理 TTL 信息

Meta 服务存储 TTL(Time To Live)可用于设置数据生命周期的定义信息。数据过期后,由 Storage 服务处理,具体流程见 TTL。

管理作业

Meta 负责创建、排队、查询和删除服务中的操作管理模块。

Graph服务

Graph 服务主要负责处理查询请求,包括分析查询句、验证句、生成执行计划和按执行计划执行四个步骤。本文将介绍这些步骤 Graph 服务。

Graph 服务架构

file.png

发送查询请求 Graph 服务结束后,将依次处理以下模块:

  1. Parser:语法解析模块。
  2. Validator:语义校验模块。
  3. Planner:执行计划和优化器模块。
  4. Executor:执行发动机模块。
Parser

Parser 接到请求后,模块通过 Flex(词法分析工具)和 Bison(语法分析工具)生成的词法语法分析器将句子转换为抽象语法树(AST),不符合语法规则的句子将在语法分析阶段被拦截。

例如GO FROM "Tim" OVER like WHERE properties(edge).likeness > 8.0 YIELD dst(edge)语句转换的 AST 如下。

file.png

Validator

Validator 生成的模块 AST 语义校验主要包括:

验证元数据信息

验证句子中的元数据信息是否正确。

例如解析 OVER、WHERE和YIELD 在句子中,会发现 Schema 校验 Edge type、Tag 是否存在信息,或在插入数据时检查插入的数据类型和 Schema 中间是否一致。

验证上下文引用的信息

检查引用的变量是否存在或引用的属性是否属于变量。

例如语句$var = GO FROM "Tim" OVER like YIELD dst(edge) AS ID; GO FROM $var.ID OVER serve YIELD dst(edge),Validator 模块首先检查变量 var 是否定义,然后检查属性 ID 它是否属于变量 var。

验证类型推断

推断表达式的结果类型,并根据句子验证类型是否正确。

例如 WHERE 子句要求结果是 bool、null 或者 empty。

校验 * 代表的信息

包含在查询语句中 * 时间,需要验证句子和句子 * 涉及的 Schema 全部验证。

例如语句GO FROM "Tim" OVER * YIELD dst(edge), properties(edge).likeness, dst(edge),在验证OVER子句时,需要验证所有OVER子句 Edge type,如果 Edge type 包含 like和serve,这句话将展开为GO FROM "Tim" OVER like,serve YIELD dst(edge), properties(edge).likeness, dst(edge)

验证输入输出

检查管道符前后的一致性。

例如语句GO FROM "Tim" OVER like YIELD dst(edge) AS ID | GO FROM $-.ID OVER serve YIELD dst(edge),Validator 模块会校验 $-.ID 是否定义了管道符左侧。

验证完成后,Validator 该模块还将生成默认执行,但未优化的执行计划并存储在目录中 src/planner 内。

Planner

若配置文件 nebula-graphd.conf 中 enable_optimizer 设置为 false,Planner 不会优化模块 Validator 模块生成的执行计划直接交给 Executor 模块执行。

若配置文件 nebula-graphd.conf中enable_optimizer 设置为 true,Planner 模块会对 Validator 优化模块生成的执行计划。如下图所示。

file.png

优化前

如上图右侧未优化的执行计划所示,每个节点依赖于另一个节点,如根节点 Project 依赖 Filter、Filter 依赖 GetNeighbor,最后找到叶节点 Start,开始执行(不是真的执行)。

在此过程中,每个节点都有相应的输入变量和输出变量,这些变量存储在哈希表中。由于实施计划没有真正实施,哈希表中的每一个都在哈希表中 key 的 value 除此之外,所有值都是空的 Start 节点,初始数据将存储在节点的输入变量中)。在仓库中定义了哈希表 nebula-graph 内的 src/context/ExecutionContext.cpp 中。

例如,哈希表的名字是 ResultMap,在建立 Filter 节点从这个节点中定义 ResultMap["GN1"] 读取数据,然后存储结果 ResultMap["Filter2"] 依次类推,确定每个节点的输入输出。

优化过程

Planner 目前模块的优化方法是 RBO(rule-based optimization),也就是说,预定义优化规则,然后对其进行处理 Validator 优化了模块生成的默认执行计划。新的优化规则 CBO(cost-based optimization)正在开发中。在仓库中存储优化代码 nebula-graph 的目录 src/optimizer/ 内。

RBO 这是一个自底向上的探索过程,即对于每个规则,它将从执行计划的根节点(例如Project)开始,一步一步地探索底部节点,并检查规则是否可以匹配。

如上图所示,探索节点 Filter 当发现节点依赖时,发现节点是 GetNeighbor,如果匹配预定义的规则,就会出现 Filter 融入到 GetNeighbor 中间,然后移除节点 Filter,继续匹配下一个规则。在执行阶段,当算子 GetNeighbor 调用 Storage 当服务界面获得一个点的邻边时,Storage 不合格的边缘将在服务内部直接过滤掉,这样传输的数据量就可以大大降低,这种优化称为过滤下推。

Executor

Executor 该模块包含调度器(Scheduler)和执行器(Executor),通过调度器调度执行计划,执行器可以根据执行计划生成相应的执行算子,从叶片节点到根节点结束。如下图所示。

file.png

每个执行计划节点对应于一个执行计算器,在优化执行计划时确定了节点的输入输出。每个计算器只需获得输入变量中的值,最后将计算结果放入相应的输出变量中,因此只需从节点中计算 Start 最后一个算子的输出变量将作为最终结果一步一步地返回到客户端。

代码结构

Nebula Graph 代码层次结构如下:

|--src   |--context    ////检查期和执行期上下文   |--daemons   |--executor   ///执行算子   |--mock   |--optimizer  //优化规则   |--parser     ///词法语法分析   |--planner    //执行计划结构   |--scheduler  //调度器   |--service   |--util       //基本组件   |--validator  ///语句校验   |--visitor
Storage服务

Nebula Graph 存储包括两部分,一部分是 Meta 相关存储,称为 Meta 上面已经介绍了服务。

另一种是与具体数据相关的存储,称为 Storage 服务。它在运行 nebula-storaged 在这个过程中。本文仅介绍 Storage 服务架构设计。

优势
  • 高性能(自研) KVStore)
  • 易水平扩展(Shared-nothing 结构,不依赖 NAS 等硬件设备)
  • 强一致性(Raft)
  • 高可用性(Raft)
  • 支持与第三方系统同步(如全文索引)
Storage 服务架构

file.png

Storage 服务是由 nebula-storaged 用户可以根据场景配置提供流程 nebula-storaged 例如,测试环境的过程量 1 一、生产环境 3 个。

所有 nebula-storaged 过程构成了基础 Raft 协议集群,整个服务架构可分为三层,从上到下依次为:

Storage interface 层

Storage 服务的顶层定义了一系列与图片相关的内容 API。API 在这一层,请求将被翻译成一组分片 KV 例如:

  • getNeighbors:查询一批点的出入边、返回边及相应属性,并支持条件过滤。
  • insert vertex/edge:插入一点或边缘及其属性。
  • getProps:获取一个点或一个边的属性。

正是这一层的存在使这一层的存在 Storage 服务变成了真实的图存储,否则 Storage 只有一个服务 KV 存储服务。

Consensus 层

Storage 实现了服务的中间层 Multi Group Raft,保证强一致性和高可用性。

Store Engine 层

Storage 服务的底层是单机版本的本地存储引擎,为本地数据提供信息、put、scan等操作。KVStoree存储相关接口.h和KVEngine.h文件,用户可根据业务需要定制本地存储插件。

KVStore

Nebula Graph 使用自我开发 KVStore,而不是其他开源 KVStore,原因如下:

  • 需要高性能 KVStore。
  • 需要以图书馆的形式提供,以实现高效的计算和推进。对于强度 Schema 的 Nebula Graph 计算下推时如何提供? Schema 信息,是效率的关键。
  • 数据需要很强的一致性。

基于上述原因,Nebula Graph 使用 RocksDB 作为本地存储引擎,实现了自己的目标 KVStore,有以下优点:

  • 对于多硬盘机,Nebula Graph 多硬盘并发能力只需配置多个不同的数据目录即可充分利用。
  • 由 Meta 统一管理所有服务 Storage 根据所有分片的分布和状态,服务可以手动平衡负载。
  • 定制预写日志(WAL),每个片段都有自己的 WAL。
  • 支持多个图形空间,不同的图形空间相互隔离,每个图形空间可以设置自己的分片数和副本数。
数据存储格式

图中存储的主要数据是点和边,Nebula Graph 将点和边的信息存储为 key,存储点和边的属性信息 value 为了更有效地使用属性过滤。

点数据存储格式

相比 Nebula Graph 2.x 版本,3.x 版本的每个点都多了一个,不包括 TagID 字段并且无 value 的 key,用于支持无 Tag 的点。

file.png

字段说明Typekey 类型。长度为 1 字节。PartID数据分片编号。长度为 3 字节。这个字段主要用于 Storage 负载均衡(balance)根据前缀扫描整个分片的数据很方便。VertexID点 ID。当点 ID 类型为 int 时,长度为 8 字节;当点 ID 类型为 string 当创建图形空间时,长度为fixed_string。TagID点关联 Tag ID。长度为 4 字节。SerializedValue序列化 value,用于保存点的属性信息。边数据存储格式

file.png

字段说明Typekey 类型。长度为 1 字节。PartID数据分片编号。长度为 3 字节。这个字段主要用于 Storage 负载均衡(balance)根据前缀扫描整个分片的数据很方便。VertexID点 ID。前一个Vertexid在出境时表示起点 ID,用入边来表示目的点 ID;后一个VertexID在边缘表示目的点 ID,表示入边的起点 ID。Edgetype边的类型。大于 0 表示出边,小于 0 表示入边。长度为 4 字节。Rank用于处理两点之间有多个相同类型的边缘。用户可以根据自己的需要设置,如存储交易时间、交易流量等。长度为 8 PlaceHolder预留字节。长度为 1 字节。SerializedValue序列化 value,用于保存边缘的属性信息。属性说明

Nebula Graph 使用强类型 Schema。

对于点或边的属性信息,Nebula Graph 按顺序存储属性信息。由于属性的长度是固定的,可以根据偏移量快速查询。在解码之前,需要从 Meta 在服务中查询具体信息 Schema 信息(并缓存)。同时,支持在线变更 Schema,在编码属性中添加相应的属性 Schema 版本信息。

数据分片

由于超大规模关系网络的节点数量高达100亿到1000亿,而边缘的节点数量将高达1万亿,即使只有存储点和边缘,也远远大于普通服务器的容量。因此,有必要切割图形元素并存储在不同的逻辑中。(Partition)上。Nebula Graph 边缘分割的方式。

file.png

切边和储存放大

Nebula Graph 逻辑上的一边对应硬盘上的两个键值(key-value pair),当边缘的数量和属性较多时,存储较大的现象更为明显。边缘的存储方法如下图所示。

file.png

上图以最简单的两点和一边为例,起点 SrcVertex 通过边 EdgeA 连接目的点 DstVertex,形成路径(SrcVertex)-[EdgeA]->(DstVertex)。这两个点和一个边会 6 存储层中保存了两个不同的分片,即以个键值对的形式存储 Partition x 和 Partition y 详细说明如下:

  • 点 SrcVertex 保存键值 Partition x 中。
  • 边 EdgeA 这里使用的第一个键值 EdgeA_Out 表示,与 SrcVertex 一同保存在 Partition x 中。key 的字段有 Type、PartID(x)、VID(Src,即点 SrcVertex 的 ID)、EdgeType(符号为正,代表边方向为出)、Rank(0)、VID(Dst,即点 DstVertex 的 ID)和 PlaceHolder。SerializedValue 即 Value,是序列化的边缘属性。
  • 点 DstVertex 保存键值 Partition y 中。
  • 边 EdgeA 这里使用的第二个键值 EdgeA_In 表示,与 DstVertex 一同保存在 Partition y 中。key 的字段有 Type、PartID(y)、VID(Dst,即点 DstVertex 的 ID)、EdgeType(符号为负,代表边方向为入)、Rank(0)、VID(Src,即点 SrcVertex 的 ID)和 PlaceHolder。SerializedValue 即 Value,它是序列化的边缘属性和 EdgeA_Out 中间部分完全相同。

EdgeA_Out 和 EdgeA_In 存储层以相反方向的两个边形式存在,两者组合成一个逻辑边 EdgeA。EdgeA_Out 用于从起点开始的遍历请求,例如(a)-[]->();EdgeA_In 用于指向目的点的遍历请求,或从目的点开始,沿边缘方向逆序进行的遍历请求,如()-[]->(a)。

如 EdgeA_Out 和 EdgeA_In 一样,Nebula Graph 多余地存储每个边缘的信息,使存储边缘所需的实际空间翻倍。由于边缘对应 key 占用的硬盘空间小,但占用的硬盘空间小 value 占用的空间与属性值的长度和数量成正比,因此,当边属性值较大或数量较大时,硬盘空间占用量较大。

如果对边操作,为了保证两个键值对的最终一致性,可以打开 TOSS 功能打开后,将先在正向边的分片上操作,然后在反向边的分片上操作,最后返回结果。

分片算法

静态采用分片策略 Hash 方式,即对点 VID 取模操作,同一点的所有 Tag、边缘和边缘信息将存储在同一片中,大大提高了查询效率。

创建图形空间时,应指定分片数量,设置后不能修改分片数量。建议在设置时提前满足未来业务扩张的需要。

多机集群部署时,分片分布在集群中的不同机器上。分片数量为 CREATE SPACE 句子中指定,以后不能更改。

若需将某些点放置在同一片段(如机器上),可参考公式或代码。

下面用简单的代码说明 VID 与分片的关系。

// 如果 ID 长度为 8,为了兼容 1.0将数据类型视为数据类型 int64。uint64_t vid = 0;        if (id.size() == 8) {        memcpy(static_cast<void*>(&vid), id.data(), 8);        } else {        MurmurHash2 hash;        vid = hash(id.data());        }        PartitionID pId = vid % numParts + 1;

简单地说,上述代码是哈希计算固定字符串,并将其转换为数据类型 int64 数字(int64 数字哈希的计算结果是数字本身),取模数字,然后添加 1,即:

pId = vid % numParts + 1;

示例的部分参数说明如下。

参数说明%取模操作。numpartsVID所在图空间的分片数,即 CREATE SPACE 语句中的partition_num值。pidVID分片 ID。

例如有 100 分片,VID为 1、101 和 1001 三点将存储在相同的分片中。分片 ID 与机器地址之间的映射是随机的,因此不能假设任何两个片段位于同一台机器上。

关于Raft Raft 的简单介绍

在分布式系统中,同一数据通常有多个副本,因此即使少数副本出现故障,系统仍然可以正常运行。这需要一些技术手段来确保多个副本之间的一致性。

基本原理:Raft 它是一种协议,用于保证多副本的一致性。Raft 通过多个副本之间的竞选,赢得“半数以上”副本投票的(候选人)副本成为 Leader,由 Leader 代表所有副本提供外部服务;其他 Follower 作为备份。当该 Leader 异常发生后(通信故障、运维命令等) Follower 新一轮选举,投票新一轮选举 Leader。Leader 和 Follower 通过心跳相互探测是否存活,并通过心跳相互探测 Raft-wal 写入硬盘的方式,超过多个心跳仍然没有反应的副本会被认为是故障。

因为 Raft-wal 如果硬盘写作能力的瓶颈会导致硬盘定期写作 Raft 心跳失败导致选举重新开始。硬盘 IO 在严重堵塞的情况下,会导致长期无法选举 Leader。

阅读和写作过程:对于客户端的每个写入请求,Leader 该写入以 Raft-wal 将这一条同步给其他条款的方式 Follower,只有“超过半数”的副本才能成功收到 Raft-wal 之后才会回客户端写成功。直接访问客户端的每个读取请求 Leader,而 Follower 不参与阅读请求服务。

故障流程:场景 1:考虑一个配置为单副本(图空间)的集群;如果系统只有一个副本,它本身就是 Leader;如果发生故障,系统将完全不可用。场景 2:考虑一个配置 3 副本(图空间)集群;如果系统有 3 其中一个副本是副本 Leader,其他 2 个副本是 Follower;即使原 Leader 如果发生故障,剩下的两个副本仍然可以投票给一个新的 Leader(以及一个 Follower),这个时候系统还可以用;但是当这个时候 2 复印件中任何一方再次出现故障后,由于投票人数不足,系统将完全不可用。

Raft 多副本的方式和 HDFS 多副本的方式不同,Raft 基于“多数派”的投票,所以副本的数量不能是偶数。

Multi Group Raft

由于 Storage 基于集群分布式架构,服务需要支持 Raft 协议实现了 Multi Group Raft,也就是说,每个片段的所有副本共同组成一个 Raft group,其中一个副本是 leader,其他副本是 follower,实现强一致性和高可用性。Raft 部分实现如下。

由于 Raft 日志不允许空洞,Nebula Graph 使用 Multi Group Raft 当分片数量较多时,可以有效地缓解这个问题 Nebula Graph 性能。但是太多的分片会增加开销,比如 Raft group 存储在内部的状态信息,WAL 当负载过低时,文件或批量操作。

实现 Multi Group Raft 有 2 个关键点:

  • 共享 Transport 层

每一个 Raft group 内部需要对应 peer 如果不能共享,发送消息 Transport 层,会导致巨大的连接成本。

  • 共享线程池

如果不共享一组线程池,系统的线程数量会过多,导致上下文切换成本大。

批量(Batch)操作

Nebula Graph 在中间,每个片段都是串行写日志。为了提高吞吐量,写日志时需要批量操作,但因为 Nebula Graph 利用 WAL 要实现一些特殊功能,需要对批量操作进行分组 Nebula Graph 的特色。

例如无锁 CAS 操作需要以前的 WAL 只有在所有提交后才能执行。如果是批量写入的 WAL 里包含了 CAS 类型的 WAL,有必要将它们分成粒度较小的组,并确保它们 WAL 串行提交。

leader 切换(Transfer Leadership)

leader 切换对负载平衡至关重要。当一个分片从一台机器转移到另一台机器时,首先检查分片是否 leader,如果是,需要先切换 leader,数据迁移完成后,通常需要重新平衡 leader 分布。

对于 leader 来说,提交 leader 切换命令时,你会放弃自己 leader 身份,当 follower 收到 leader 选举将在切换命令时启动。

成员变更

为了避免脑裂,当一个 Raft group 当成员发生变化时,他们需要在新旧状态下有一个中间状态 group 大多数派需要重叠的部分,以防止新的 group 或旧的 group 单方面做决定。为了更简化,Diego Ongaro 每次在自己的博士论文中提出只增减一篇 peer 为了保证新旧 group 大多数派总是重叠的。Nebula Graph 也采用了这种方法,但增加成员和移除成员之间存在差异。具体实现方式请参见 Raft Part class 里 addPeer/removePeer 的实现。

与 HDFS 的区别

Storage 服务基于 Raft 协议实现的分布式架构和 HDFS 分布式结构存在一些差异。例如:

  • Storage 通过服务本身 Raft 协议保证一致性,复印件的数量通常是奇数,便于选举 leader,而 HDFS 存储具体数据 DataNode 需要通过 NameNode 对副本数量没有要求,以确保一致性。
  • Storage 服务只有 leader 提供读写服务的副本, HDFS 所有副本均可提供读写服务。
  • Storage 服务不能修改副本数量,只能在创建图形空间时指定副本数量 HDFS 副本的数量可以调整。
  • Storage 服务是直接访问文件系统, HDFS 上层(例如 HBase)需要先访问 HDFS,然后访问文件系统,远程过程调用(RPC)次数更多。

总而言之,Storage 服务更轻量级,简化了一些功能,没有架构 HDFS 复杂,能有效提高小块存储的读写性能。

参考文章
  • https://docs.nebula-graph.com.cn/3.0.1/

文章收录于 GitHub仓库Javadeveloperbrainnnninnninnbraining [Java工程师必备+学习+知识点+面试]:包括计算机网络知识,JavaSE、JVM、Spring、Springboot、SpringCloud、Mybatis、多线程并发,netty、MySQL、MongoDB、Elasticsearch、Redis、HBASE、RabbitMQ、RocketMQ、Pulsar、Kafka、Zookeeper、Linux、设计模式,智力问题,项目结构,分布式相关,算法,面试问题