本文由美团 NLP 团队高辰、赵登昌撰写

首发于 Nebula Graph 官方论坛:https://discuss.nebula-graph.com.cn/t/topic/1377

1. 前言

近年来,深度学习和知识图谱技术发展迅速,相比于深度学习的“黑盒子”,知识图谱具有很强的可解释性,在搜索推荐、智能助理、金融风控等场景中有着广泛的应用。美团基于积累的海量业务数据,结合使用场景进行充分地挖掘关联,逐步建立起包括美食图谱、旅游图谱、商品图谱在内的近十个领域知识图谱,并在多业务场景落地,助力本地生活服务的智能化。

为了高效存储并检索图谱数据,相比传统关系型数据库,选择图数据库作为存储引擎,在多跳查询上具有明显的性能优势。当前业界知名的图数据库产品有数十款,选型一款能够满足美团实际业务需求的图数据库产品,是建设图存储和图学习平台的基础。我们结合业务现状,制定了选型的基本条件:

  • 开源项目,对商业应用友好

    • 拥有对源代码的控制力,才能保证数据安全和服务可用性。
  • 支持集群模式,具备存储和计算的横向扩展能力
    • 美团图谱业务数据量可以达到千亿以上点边总数,吞吐量可达到数万 qps,单节点部署无法满足存储需求。
  • 能够服务 OLTP 场景,具备毫秒级多跳查询能力
    • 美团搜索场景下,为确保用户搜索体验,各链路的超时时间具有严格限制,不能接受秒级以上的查询响应时间。
  • 具备批量导入数据能力
    • 图谱数据一般存储在 Hive 等数据仓库中。必须有快速将数据导入到图存储的手段,服务的时效性才能得到保证。

我们试用了 DB-Engines 网站上排名前 30 的图数据库产品,发现多数知名的图数据库开源版本只支持单节点,不能横向扩展存储,无法满足大规模图谱数据的存储需求,例如:Neo4j、ArangoDB、Virtuoso、TigerGraph、RedisGraph。经过调研比较,最终纳入评测范围的产品为:NebulaGraph(原阿里巴巴团队创业开发)、Dgraph(原 Google 团队创业开发)、HugeGraph(百度团队开发)。

2. 测试概要

2.1 硬件配置

  • 数据库实例:运行在不同物理机上的 Docker 容器。
  • 单实例资源:32 核心,64GB 内存,1TB SSD 存储。【Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz】
  • 实例数量:3

2.2 部署方案

Metad 负责管理集群元数据,Graphd 负责执行查询,Storaged 负责数据分片存储。存储后端采用 RocksDB。

实例 1 实例 2 实例 3
Metad Metad Metad
Graphd Graphd Graphd
Storaged[RocksDB] Storaged[RocksDB] Storaged[RocksDB]

Zero 负责管理集群元数据,Alpha 负责执行查询和存储。存储后端为 Dgraph 自有实现。

实例 1 实例 2 实例 3
Zero Zero Zero
Alpha Alpha Alpha

HugeServer 负责管理集群元数据和查询。HugeGraph 虽然支持 RocksDB 后端,但不支持 RocksDB 后端的集群部署,因此存储后端采用 HBase。

实例1 实例2 实例3
HugeServer[HBase] HugeServer[HBase] HugeServer[HBase]
JournalNode JournalNode JournalNode
DataNode DataNode DataNode
NodeManager NodeManager NodeManager
RegionServer RegionServer RegionServer
ZooKeeper ZooKeeper ZooKeeper
NameNode NameNode[Backup] -
- ResourceManager ResourceManager[Backup]
HBase Master HBase Master[Backup] -

3. 评测数据集

  • 社交图谱数据集:https://github.com/ldbc011

    • 生成参数:branch=stable, version=0.3.3, scale=1000
    • 实体情况:4 类实体,总数 26 亿
    • 关系情况:19 类关系,总数 177 亿
    • 数据格式:csv
    • GZip 压缩后大小:194 G

4. 测试结果

4.1 批量数据导入

4.1.1 测试说明

批量导入的步骤为:Hive 仓库底层 csv 文件 -> 图数据库支持的中间文件 -> 图数据库。各图数据库具体导入方式如下:

  • Nebula:执行 Spark 任务,从数仓生成 RocksDB 的底层存储 sst 文件,然后执行 sst Ingest 操作插入数据。
  • Dgraph:执行 Spark 任务,从数仓生成三元组 rdf 文件,然后执行 bulk load 操作直接生成各节点的持久化文件。
  • HugeGraph:支持直接从数仓的 csv 文件导入数据,因此不需要数仓-中间文件的步骤。通过 loader 批量插入数据。

4.1.2 测试结果

4.1.3 数据分析

  • Nebula:数据存储分布方式是主键哈希,各节点存储分布基本均衡。导入速度最快,存储放大比最优。
  • Dgraph:原始 194G 数据在内存 392G 的机器上执行导入命令,8.7h 后 OOM 退出,无法导入全量数据。数据存储分布方式是三元组谓词,同一种关系只能保存在一个数据节点上,导致存储和计算严重偏斜。
  • HugeGraph:原始 194G 的数据执行导入命令,写满了一个节点 1,000G 的磁盘,造成导入失败,无法导入全量数据。存储放大比最差,同时存在严重的数据偏斜。

4.2 实时数据写入

4.2.1 测试说明

  • 向图数据库插入点和边,测试实时写入和并发能力。

    • 响应时间:固定的 50,000 条数据,以固定 qps 发出写请求,全部发送完毕即结束。取客户端从发出请求到收到响应的 Avg、p99、p999 耗时。
    • 最大吞吐量:固定的 1,000,000 条数据,以递增 qps 发出写请求,Query 循环使用。取 1 分钟内成功请求的峰值 qps 为最大吞吐量。
  • 插入点
    • Nebula
      1. INSERT VERTEX t_rich_node (creation_date, first_name, last_name, gender, birthday, location_ip, browser_used) VALUES ${mid}:('2012-07-18T01:16:17.119+0000', 'Rodrigo', 'Silva', 'female', '1984-10-11', '84.194.222.86', 'Firefox')
    • Dgraph
      1. {
      2. set {
      3. <${mid}> <creation_date> "2012-07-18T01:16:17.119+0000" .
      4. <${mid}> <first_name> "Rodrigo" .
      5. <${mid}> <last_name> "Silva" .
      6. <${mid}> <gender> "female" .
      7. <${mid}> <birthday> "1984-10-11" .
      8. <${mid}> <location_ip> "84.194.222.86" .
      9. <${mid}> <browser_used> "Firefox" .
      10. }
      11. }
    • HugeGraph
      1. g.addVertex(T.label, "t_rich_node", T.id, ${mid}, "creation_date", "2012-07-18T01:16:17.119+0000", "first_name", "Rodrigo", "last_name", "Silva", "gender", "female", "birthday", "1984-10-11", "location_ip", "84.194.222.86", "browser_used", "Firefox")
  • 插入边
    • Nebula
      1. INSERT EDGE t_edge () VALUES ${mid1}->${mid2}:();
    • Dgraph
      1. {
      2. set {
      3. <${mid1}> <link> <${mid2}> .
      4. }
      5. }
    • HugeGraph
      1. g.V(${mid1}).as('src').V(${mid2}).addE('t_edge').from('src')

4.2.2 测试结果

  • 实时写入

4.2.3 数据分析

  • Nebula:如 4.1.3 节分析所述,Nebula 的写入请求可以由多个存储节点分担,因此响应时间和吞吐量均大幅领先。
  • Dgraph:如 4.1.3 节分析所述,同一种关系只能保存在一个数据节点上,吞吐量较差。
  • HugeGraph:由于存储后端基于 HBase,实时并发读写能力低于 RocksDB(Nebula)和 BadgerDB(Dgraph),因此性能最差。

4.3 数据查询

4.3.1 测试说明

  • 以常见的 N 跳查询返回 ID,N 跳查询返回属性,共同好友查询请求测试图数据库的读性能。

    • 响应时间:固定的 50,000 条查询,以固定 qps 发出读请求,全部发送完毕即结束。取客户端从发出请求到收到响应的 Avg、p99、p999 耗时。

      • 60s 内未返回结果为超时。
    • 最大吞吐量:固定的 1,000,000 条查询,以递增 qps 发出读请求,Query 循环使用。取 1 分钟内成功请求的峰值 qps 为最大吞吐量。
    • 缓存配置:参与测试的图数据库都具备读缓存机制,默认打开。每次测试前均重启服务清空缓存。
  • N 跳查询返回 ID
    • Nebula
      1. GO ${n} STEPS FROM ${mid} OVER person_knows_person
    • Dgraph
      1. {
      2. q(func:uid(${mid})) {
      3. uid
      4. person_knows_person { #${n}跳数 = 嵌套层数
      5. uid
      6. }
      7. }
      8. }
    • HugeGraph
      1. g.V(${mid}).out().id() #${n}跳数 = out()链长度
  • N 跳查询返回属性
    • Nebula
      1. GO ${n} STEPS FROM ${mid} OVER person_knows_person YIELDperson_knows_person.creation_date, $$.person.first_name, $$.person.last_name, $$.person.gender, $$.person.birthday, $$.person.location_ip, $$.person.browser_used
    • Dgraph
      1. {
      2. q(func:uid(${mid})) {
      3. uid first_name last_name gender birthday location_ip browser_used
      4. person_knows_person { #${n}跳数 = 嵌套层数
      5. uid first_name last_name gender birthday location_ip browser_used
      6. }
      7. }
      8. }
    • HugeGraph
      1. g.V(${mid}).out() #${n}跳数 = out()链长度
  • 共同好友查询语句
    • Nebula
      1. GO FROM ${mid1} OVER person_knows_person INTERSECT GO FROM ${mid2} OVER person_knows_person
    • Dgraph
      1. {
      2. var(func: uid(${mid1})) {
      3. person_knows_person {
      4. M1 as uid
      5. }
      6. }
      7. var(func: uid(${mid2})) {
      8. person_knows_person {
      9. M2 as uid
      10. }
      11. }
      12. in_common(func: uid(M1)) @filter(uid(M2)){
      13. uid
      14. }
      15. }
    • HugeGraph
      1. g.V(${mid1}).out().id().aggregate('x').V(${mid2}).out().id().where(within('x')).dedup()

4.3.2 测试结果

  • N 跳查询返回 ID

  • N 跳查询返回属性

单个返回节点的属性平均大小为 200 Bytes。

  • 共同好友

    本项未测试最大吞吐量。

4.3.3 数据分析

  • 在 1 跳查询返回 ID「响应时间」实验中,Nebula 和 DGraph 都只需要进行一次出边搜索。由于 DGraph 的存储特性,相同关系存储在单个节点,1 跳查询不需要网络通信。而 Nebula 的实体分布在多个节点中,因此在实验中 DGraph 响应时间表现略优于 Nebula。
  • 在 1 跳查询返回 ID「最大吞吐量」实验中,DGraph 集群节点的 CPU 负载主要落在存储关系的单节点上,造成集群 CPU 利用率低下,因此最大吞吐量仅有 Nebula 的 11%。
  • 在 2 跳查询返回 ID「响应时间」实验中,由于上述原因,DGraph 在 qps=100 时已经接近了集群负载能力上限,因此响应时间大幅变慢,是 Nebula 的 3.9 倍。
  • 在 1 跳查询返回属性实验中,Nebula 由于将实体的所有属性作为一个数据结构存储在单节点上,因此只需要进行【出边总数 Y】次搜索。而 DGraph 将实体的所有属性也视为出边,并且分布在不同节点上,需要进行【属性数量 X * 出边总数 Y】次出边搜索,因此查询性能比 Nebula 差。多跳查询同理。
  • 在共同好友实验中,由于此实验基本等价于 2 次 1 跳查询返回 ID,因此测试结果接近,不再详述。
  • 由于 HugeGraph 存储后端基于 HBase,实时并发读写能力低于 RocksDB(Nebula)和 BadgerDB(Dgraph),因此在多项实验中性能表现均落后于 Nebula 和 DGraph。

5. 结论

参与测试的图数据库中,Nebula 的批量导入可用性、导入速度、实时数据写入性能、数据多跳查询性能均优于竞品,因此我们最终选择了 Nebula 作为图存储引擎。

6. 参考资料

本次性能测试系美团 NLP 团队高辰、赵登昌撰写,如果你对本文有任意疑问,欢迎来原贴和作者交流:https://discuss.nebula-graph.com.cn/t/topic/1377

主流开源分布式图数据库 Benchmark的更多相关文章

  1. 分布式图数据库 Nebula RC2 发布:增强了 CSV Importer 功能

    Nebula Graph 是开源的分布式图数据库,可应用于知识图谱.社交推荐.风控.IoT 等场景. 本次 RC2 主要新增 GO FROM ... REVERSELY 和 GROUP BY 等语句, ...

  2. 分布式图数据库 Nebula Graph 的 Index 实践

    导读 索引是数据库系统中不可或缺的一个功能,数据库索引好比是书的目录,能加快数据库的查询速度,其实质是数据库管理系统中一个排序的数据结构.不同的数据库系统有不同的排序结构,目前常见的索引实现类型如 B ...

  3. 初识分布式图数据库 Nebula Graph 2.0 Query Engine

    摘要:本文主要介绍 Query 层的整体结构,并通过一条 nGQL 语句来介绍其通过 Query 层的四个主要模块的流程. 一.概述 分布式图数据库 Nebula Graph 2.0 版本相比 1.0 ...

  4. 分布式图数据库 Nebula Graph 中的集群快照实践

    1 概述 1.1 需求背景 图数据库 Nebula Graph 在生产环境中将拥有庞大的数据量和高频率的业务处理,在实际的运行中将不可避免的发生人为的.硬件或业务处理错误的问题,某些严重错误将导致集群 ...

  5. HBase -- 基于HDFS的开源分布式NoSQL数据库

    HBase(Hadoop Database)是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,我们可以利用HBase技术在廉价的PC上搭建起大规模结构化存储集群.同Google的Bigtable ...

  6. OPPO 图数据库平台建设及业务落地

    本文首发于 OPPO 数智技术公众号,WeChat ID: OPPO_tech 1.什么是图数据库 图数据库(Graph database)是以图这种数据结构存储和查询的数据库.与其他数据库不同,关系 ...

  7. 高性能内存图数据库RedisGraph(一)

    作为一种简单.通用的数据结构,图可以表示数据对象之间的复杂关系.生物信息学.计算机网络和社交媒体等领域中产生的大量数据,往往是相互连接.关系复杂且低结构化的,这类数据对传统数据库而言十分棘手,一个简单 ...

  8. 小试国产开源HTAP分布式NewSQL数据库TiDB-v5.3.0

    概述 定义 TiDB官网 https://pingcap.com/zh/ 最新版本为5.3.0 TiDB GitHub源码 https://github.com/pingcap/tidb TiDB是由 ...

  9. JanusGraph : 图和图数据库的简介

    JanusGraph:图数据库系统简介 图(graph)是<数据结构>课中第一次接触到的一个概念,它是一种用来描述现实世界中个体和个体之间网络关系的数据结构. 为了在计算机中存储图,< ...

随机推荐

  1. spring cloud微服务快速教程之(十二) 分布式ID解决方案(mybatis-plus篇)

    0-前言 分布式系统中,分布式ID是个必须解决的问题点: 雪花算法是个好方式,不过不能直接使用,因为如果直接使用的话,需要配置每个实例workerId和datacenterId,在微服务中,实例一般动 ...

  2. Kubernetes K8S之Service服务详解与示例

    K8S之Service概述与代理说明,并详解所有的service服务类型与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master Cent ...

  3. js图形打印

    1. 打印等边三角形 document.writeln("打印三角形</br>"); for(var i=0;i<5;i++){ for(var j=5;j> ...

  4. Jakartase_IO流_ — Commons IO_(IO流终极篇)

    一.前言 Apache Commons IO是Apache基金会创建并维护的Java函数库. 它提供了许多类使得开发者的常见任务变得简单,同时减少重复代码 二.Commons IO 类库 2.1 Fi ...

  5. 10.Atomic-原子性操作

  6. Redis 发布订阅,小功能大用处,真没那么废材!

    今天小黑哥来跟大家介绍一下 Redis 发布/订阅功能. 也许有的小伙伴对这个功能比较陌生,不太清楚这个功能是干什么的,没关系小黑哥先来举个例子. 假设我们有这么一个业务场景,在网站下单支付以后,需要 ...

  7. java学习从“菜鸟”到“放弃”

    今天学到java的对象和类中, 由于刚考完c++面向对象与程序设计这门课,对于c++中的类掌握自认为不错,就开始过渡到java. 今天面对的问题,在书写一个类的时候,发现了许多与c++不同的地方. 比 ...

  8. php+html实现用户登录退出

    随着渗透学习,逐渐意识到了学会开发也是非常重要的,仅仅是看懂感觉还是差了一些,所以写一写php的开发,这套程序目前并未有较完整的功能,之后会不断进行完善 登录页面.html <!DOCTYPE ...

  9. Egg.js学习

    egg.js是什么 是一个node.js的后台web框架,类似的还有express,koa 优势:规范.插件机制Egg.js约定了一套代码目录结构(配置config.路由router.扩展extend ...

  10. linux操作系统网卡漂移导致网络不可用

    1.故障描述 公司有100-150台服务器安装RHEL7.4&中标麒麟7.4系统,为方便编辑配置网卡,使用脚本方式配置为biosname=0,ifname=0,目的是为将en1o2p此类长字符 ...