NebulaGraph

NebulaGraph 查看完整档案

填写现居城市  |  填写毕业院校杭州欧若数网  |  图数据库 编辑 github.com/vesoft-inc/nebula 编辑
编辑

NebulaGraph:一个开源的分布式图数据库。欢迎来 GitHub 交流:https://github.com/vesoft-inc/nebula

个人动态

NebulaGraph 发布了文章 · 4月22日

手把手教你从数据预处理开始体验图数据库

封面图

本文首发于 Nebula 公众号:手把手教你从数据预处理开始体验图数据库,由社区用户 Jiayi98 供稿,分享了她离线部署 Nebula Graph、预处理 LDBC 数据集的经验,是个对新手极度友好的手把手教你学 Nebula 分享。

这不是一个标准的压力测试,而是通过一个小规模的测试帮助我熟悉 Nebula 的部署,数据导入工具,查询语言,Java API,数据迁移,以及集群性能的一个简单了解。

准备

所有的准备都需要找个有网的环境

  1. docker RPM 包 https://docs.docker.com/engine/install/centos/#install-from-a-package
  2. docker-compose tar 包 https://github.com/docker/compose/releases
  3. 提前下载镜像 https://hub.docker.com/search?q=vesoft&type=image,将 metad、graphd、storaged、console、studio、http-gateway、http-client、nginx、importer(用 docker save xxx 命令将拉好的镜像导出成 tar 包)
  4. 配置文件 https://github.com/vesoft-inc/nebula-docker-compose/blob/docker-swarm/docker-stack.yaml
  5. nebula-studio GitHub 上下载 zip 包 https://github.com/vesoft-inc/nebula-web-docker

安装

  1. 安装 Docker:
$ rpm -ivh <rpm包>
$ systemctl start docker --启动
$ systemctl status docker --查看状态
  1. 安装 docker-compose
$ mv docker-compose /usr/local/bin/ --把docker-compose文件移动到/usr/local/bin
$ chmod a+x /usr/local/bin/docker-compose --改权限
$ docker-compose -version
  1. 导入镜像
$ docker load <镜像tar包>
$ docker image ls
  1. 在机器 manager machine 上执行以下命令初始化 Docker Swarm 集群:
$ sudo docker swarm init --advertise-addr <manager machine ip>
  1. 根据提示在另一台服务器上以 worker 的身份 join swarm
$ docker node ls
  • 添加 worker node 如果出现以下报错:

Error response from daemon: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 172.16.9.129:2377: connect: no route to host"

一般是防火墙未关闭导致的(用以下方式关闭防火墙)。

$ systemctl status firewalld.service
$ systemctl disable firewalld.service
  1. 在 manager 节点上改写 docker-stack.yml,并创建 nebula.env
-- nebula.env
TZ=UTC
USER=root
  • Yaml file 里的 hostname 多台机器不可同名,启动时的错误多半是因为配置文件写得有问题,v1 升级 v2 也只需要把配置文件里的镜像换一下就可以了。
  1. 在 manager 节点上动 nebula 集群
$ docker stack deploy <stack name> -c docker-stack.yml

这里附带一些我 Debug / 检查方法:

$ docker service ls --查看服务状态
$ docker service ps <NAME/ID> --查看某一个具体的状态
$ docker stack ps --no-trunc <stack name> --查看 stack 里所有的进程
  1. 安装 Studio

代码文件夹里是 v1,有一个 v2 的文件夹里是 v2

$ cd nebula-web-docker

$ cd nebula-web-docker/v2
$ docker-compose up -d -- 构建并启动 Studio 服务; 

其中,-d 表示在后台运行服务容器

启动成功后,在浏览器地址栏输入:http://ip address:7001

测试

我用的 LDBC。

准备

  1. 获取源码 https://github.com/ldbc/ldbc_snb_datagen/tree/stable,scale factor 1-1000 用 stable branch。
  2. 下载 hadoop-3.2.1.tar.gz: http://archive.apache.org/dist/hadoop/core/hadoop-3.2.1/
  3. LDBC 数据预处理

LDBC 数据预处理

这里需要说明一下,要注意你用的 nebula 版本是否支持 “|” 作为分隔符

ldbc 的所有 vertex 和 edge 的 ID / index 都有问题,需要处理一下使得所有 vertex 的 ID 变为 unique key。

我的做法是每个 vertex 我都给一个前缀,比如 person,原始 ID 为 933,变为 p933。(为了试用一下我自己搭的 CDH 我用 Spark 做的数据预处理,处理过的数据放在 HDFS 以便后面用 nebula-exchange 导入)

硬件资源

硬件资源

备注:Nebula 不推荐使用 HDD,但我也没有 SSD, 最后测试结果证明 HDD 真的很弱。

服务分布

3 节点,服务分布如下

  • 192.168.1.10 meta,storage
  • 192.168.1.12 graph,meta,storage
  • 192.168.1.60 graph,meta,storage

2 图空间:

  1. csv:10 个 partition

    1. 原始数据约 42 M
    2. 7 千多个点,40 万条边
  2. test:100 个 partition

    1. 原始数据约 73 G
    2. 1.1 亿多个点,28.2 亿多条边(Edge: 1,101,535,334;Vertex: 282,612,309)

导入 Nebula 之后,占用储存空间共约 76 G,其中 wal 文件占 2.2 G 左右。

没有做导入的测试,一部分用了 Nebula-Importer 导入,一部分用了 Exchange 导入:

数据导入结果

开始测试

测试方法:

  1. 选取 1000 个 vertex,进行 1000 次查询的平均值

数据测试结果

  • 三度超时是将 timeout 参数调高至 120 秒后的结果,后来在终端执行了一次三度发现要三百多秒。

最后,希望这份文档对和我一样的小白们有帮助,也感谢一直以来社区和官方的答疑解惑。

Nebula 真的让用户感到真的非常 supportive,在学习使用 Nebula 的过程中我也收获了很多~

进一步交流

交流图数据库技术?加入 Nebula 交流群请先填写下你的 Nebulae 名片,Nebula 小助手会拉你进群~~

要不要看看【美团的图数据库系统】、【微众银行的数据治理方案】以及其他大厂的风控、知识图谱实践?Follow Nebula 公众号NebulaGraphCommunity 回复「PPT」即可习得大厂实践技能 ^^

推荐阅读

查看原文

赞 0 收藏 0 评论 0

NebulaGraph 发布了文章 · 4月15日

图查询语言的历史回顾短文

graph-query-language-review

本文首发于 Nebula 公众号:图查询语言的历史回顾短文

前言

最近在对图查询语言 GQL 和国际标准草案做个梳理,调研过程中找到下面这篇 mark 了没细看的旧文(毕竟收藏就是看过)。做个简单的记录。

摘要

本短文会涉及到的图查询语言有 Cypher、Gremlin、PGQL 和 G-CORE。

背景

本文主要摘录翻译自 [Tobias2018] (见参考文献),并未涉及到 SPARQL 和 RDF,只讨论了属性图。

文章撰写的时间是 2018 年,可以看做 GQL(Graph Query Language)的一些前期准备。GQL 有多个相关的起源,参见下面这张图。

graph-query-language-review

因为 Cypher 的历史和 Neo4j 紧密相关,本文会提一些 Neo4j 早期的历史。[Angles2008](见参考文献)和 [Wood2012](见参考文献)是两个不错的关于图模型和图查询语言的总结。

年表简述

  • 2000 年,Neo4j 的创始人产生将数据建模成网络(network)的想法。
  • 2001 年,Neo4j 开发了最早的核心部分代码。
  • 2007 年,Neo4j 以一个公司的方式运作。
  • 2009 年,Neo4j 团队借鉴 XPath 作为图查询语言,Gremlin 最初也是基于这个想法。
  • 2010 年,Neo4j 和 Marko Rodriguez 采用术语属性图(Property Graph)来描述 Neo4j 和 Tinkerpop / Gremlin 的数据模型。[PG2010](见参考文献)
  • 2011 年,第一个公开发行版本 Neo4j 1.4 发布了第一个版本的 Cypher。
  • 2012 年,Neo4j 1.8 为 Cypher 增加写入图的能力。
  • 2012 年,Neo4j 2.0 增加了标签和索引,Cypher 成为声明式的语言。
  • 2015 年,Oracle 为 PGX 发明查询语言 PGQL。
  • 2015 年,Neo4j 将 Cypher 开源为 openCypher。
  • 2015 年,LDBC 成立图查询语言工作组。
  • 2016 年,LDBC 工作组开始设计 G-CORE。
  • 2017 年,WG3 工作组开始讨论如何将属性图查询能力引入 SQL。
  • 2017 年,LDBC 工作组完成了 G-CORE 的初始设计 [GCORE2018](见参考文献)。
  • 2018 年,Cypher 形式化语义的论文发表 [Cypher2018] (见参考文献)。

Gremlin、Cypher、PGQL 和 G-CORE 的演进

Neo4j 的早期历史

Neo4j 和属性图这种数据模型,最早构想于 2000 年。Neo4j 的创始人们当时在开发一个媒体管理系统,所使用的数据库的 schema 经常会发生重大变化。为了支持这种灵活性,Neo4j 的联合创始人 Peter Neubauer,受 Informix Cocoon 的启发,希望将系统建模为一些概念相互连接的网络。印度理工学院孟买分校的一群研究生们实现了最早的原型。Neo4j 的联合创始人 Emil Eifrém 和这些学生们花了一周的时间,将 Peter 最初的想法扩展成为这样一个模型:节点通过关系连接,key-value 作为节点和关系的属性。这群人开发了一个 Java API 来和这种数据模型交互,并在关系型数据库之上实现了一个抽象层。

虽然这种网络模型极大的提高了生产力,但是性能一直很差。所以 Neo4j 联合创始人 Johan Svensson 花精力,为这种网络模型实现了一个原生的数据管理系统。这个就成为了 Neo4j。 在最初的几年,Neo4j 作为一个内部产品很成功。在 2007 年,Neo4j 的知识产权转移给了一家独立的数据库公司。

Neo4j 的第一个公开发行版中,数据模型由节点和有类型的边构成,节点和边都有 key-value 组成的属性。Neo4j 的早期版本没有任何的索引,应用程序只能从根节点开始自己构造查询结构(search structure)。因为这样对于应用程序非常笨重,Neo4j 2.0(2013 年 12 月发布)引入了一个新概念——点上的标签(label)。基于点标签,Neo4j 可以为一些预定义的节点属性建立索引。

节点、关系、属性、关系只能有一个标签、节点可以有零个或者多个标签,以上这些构成了 Neo4j 属性图的数据模型定义。后来增加的索引功能,让 Cypher 成为了与 Neo4j 交互的主要方式。因为这样应用开发者只需要关注于数据本身,而不是上段提到的那个开发者自己构建的查询结构(search structure)。

图或者说网络类型的数据模型(多对多的关系)和其数据库的历史,可以追溯到 80 年代。见 Kleppmann 2017第二章(见参考文献部分)。

Gremlin 的创造

最初与 Neo4j 的查询方式是通过 Java API。应用程序可以将查询引擎作为库嵌入到应用程序中,然后使用 API 查询图。如果是自定义查询引擎,然后应用程序远程访问服务器,这样就比较困难。

就在这段时间,NOSQL 这个概念开始出现。NOSQL 型的数据库引擎一般用 REST 和 HTTP 来交互和查询。Neo4j 的早期员工 Tobias Lindaaker(和 Ivarsson)考虑用 HTTP 的方式访问 Neo4j 会是一种更好的办法。他们观察到很多的查询语句可以表达为:图到树的投影映射(projection)。典型的,从根节点开始遍历一个扩张树(spanning tree),然后返回叶子节点。基于这样的观察,并参考一些树结构的查询语句,比如 XPath,也许可以作为一种图的查询方式。而且,XPath 的语法和一般 URI 的语法很像。这样 XPath 查询可以很自然的作为 HTTP GET 中 URI 的一部分。Neo4j 的联合创始人 Peter Neubauer 喜欢这个想法,找了一个朋友 Marko Rodriguez 来干。两天后,Marko 做了一个原型,用 XPath 作为图查询,Groovy 提供循环结构,分支,和计算。 这个就是 Gremlin 最初的原型。 2009 年 11 月发布了第一个版本。

后来,Marko 发现同时用两种不同的解析器(XPath和Groovy)有很多问题,就将 Gremlin 改为基于 Groovy 的一种内置的领域特定语言(DSL)。

Cypher 的创造

Gremlin 和 Neo4j 的 Java API 一样,最初用于表达如何查询数据库的一种过程(Procedural)。它允许更短的语法来表达查询,也允许通过网络远程访问数据库。Gremlin 这种过程式的特性,需要用户知道如何采用最好的办法查询结果,这样对于应用程序开发人员来说仍旧有负担。基于声明式语言 SQL 的成功:SQL 可以将获取数据的声明方式和引擎如何获取数据分开,Neo4j 的工程师们希望开发一种声明式的图查询语言。

2010 年,Andrés Taylor 作为工程师加入 Neo4j,在此之前他是 SQL DBA。他开始了一个项目,受 SQL 启发,其目标是开发图查询语言,而这种新语言 Cypher 于 2011 年 Neo4j 1.4 发布。

Cypher 的语法基础,是用 "ascii 艺术(ascii art)" 来描述图模式。这种方式最初来源于 Neo4j 工程师团队在源代码中评注如何描述图模式。可以看下图的例子:

graph-query-language-review

ascii art 简单说,就是如何用可打印文本来描述点和边。Cypher 文本用()表示点,-[]->表示边。(query)--[modeled as]->(drawing) 来表示起点 query,终点 drawing,边 modeled as。

对于程序员来说,当然可以设计一个 API 表示操作点,一个 API 表示操作边. 但这样就不是 SQL 这种声明式语言的目的,对于非程序员使用也太困难。

Cypher 第一个版本实现了对图的读取,但是需要用户说明从哪些节点开始查询。只有从这些节点开始,才可以支持图的模式匹配。

略微学术一点的说, graph patterns(或者 motifs finding)和 navigational expression 是两种不同的图查询操作Renzo2017(见参考文献部分)。不同语言这两种操作的语义是有一定差别的。

在后面的版本,2012 年 10 月发布的 Neo4j 1.8 中,Cypher 增加了修改图的能力。但查询还是需要指明从哪些节点开始。

2013 年 12 月,Neo4j 2.0 引入了 label 的概念,label 本质上是个索引。这样,查询引擎就可以利用索引,来选择模式所匹配到的节点,而不需要用户指定开始查询的节点。

有没有熟悉的感觉?

随着 Neo4j 的普及,Cypher 有着广泛的开发者群体 ,和各行各业的使用。

图查询语言也有声明式(Decalarative)和命令式(Imperative)的区别,前者(Cypher)相比后者(Gremlin)更强调目的和手段分离,更依赖于执行优化。但在工程上,两者思想并没有那么大的区别,后者也会有延迟计算和优化,前者也可以部分命令式的思想。好的优化在工程上并不是那么容易,专业的用户比查询引擎更清楚如何求取、访问、加工数据Renzo2017。

openCypher - 一种推进和标准化Cypher的开源过程

2015 年 9 月,Neo4j 开放了 Cypher 查询语言,通过开源的方式来治理。这个新主体的治理主体是 openCypher Implementors Group(oCIG)。

2016 年,openCypher 项目发布 EBNF 和 ANTLR4 ,Neo4j 也贡献了很多基于 Apache 2.0 的语言功能测试集(openCypher Technology Compatibility Kit, TCK)。将这些作为语言标准定义,任何人都可以为该语言提交新的提议。2016 年,SAP HANA Graph 发布了基于 Cypher 的查询部分的实现,Agens Graph 和 Redis Graph 在 2017 年支持了 Cypher。2017 年 oCIG 也进行了一系列线上线下的会议,讨论语言功能扩展等。

这个治理和讨论方式可以通过如下方式:https://github.com/opencypher/openCypher/blob/master/CIP-PROCESS.adoc

oCIG 发布 openCypher,采用英语语言作为规范,对应了 Neo4j 3.3.0 版本中的 Cypher。

PGQL 的创建

2015 年,Oracle 为 PGX 引擎开发了图查询语言 PGQL。PGQL 受 Cypher 的启发,也和 Cypher 很接近。

G-CORE 的创建

Linked Data Benchmarking Council(LDBC)定义了一种厂商无关的基准测试。在开发这个基准测试的过程中,他们发现市面上没有标准的查询语言来表达图查询。为了处理这个问题,成立了一个特别工作组,调研市面上已经存在的图查询语言和框架,定义图查询必须的功能,然后为现有语言提供修改建议。

2016 年,他们想设计一种新语言,而不是对于现有语言的修改。主要原因是不想受现有语言的模型的限制。

G-CORE 是由 LDBC 工作组设计的,但主要受 Cypher 的启发,采用一样的语义。

结论

Cypher 是 PGQL 和 G-CORE 的共同祖先。这几个语言的语法和语义都非常的接近。PGQL 更接近一些早期的 Cypher,而 G-CORE 更期望语法和语义上都与 Cypher 兼容。

一些个人看法

除去学术上的探索和一些零散的工程尝试,以 Cypher 作为主流属性图查询语言工程实践的历史基准,也就 10 年的时间。在前面的几年 2010-2013,Cypher 自身在基础图功能上还有不少缺失,比如索引、图模式,迭代到 2014 年才产生当前使用的一个主流版本,并且还在持续演化 [Nadime2018](见参考文献部分)。

重头全新设计一种图语言其实很难,很可能会把前人走过的路(坑)再走(掉)一遍(G-CORE 和 nGQL)。这是 GQL 第一个不容易的地方。

一个标准化组织中,有学术和商业机构,各自诉求也很不相同,商业机构已经各自有庞大的商业使用群体,这是第二个不容易的地方。

制定完的标准,最后是否会被市场所接受和采纳(vendor、custormer、developer、 goverment),又需要多少的布道。这是 GQL 第三个不容易的地方。

除了核心基础的图操作以外的特性,比如 SQL、pregel [Grzegorz2010](见参考文献部分)、GNN,每个语言 PGQL、GSQL 甚至 Neo4j 自己都各自采用了完全不同的方式来支持这些特性,要不要考虑这些新出现的特性。这是 GQL 第四个不容易的地方。

再想想 GQL 和 Apache Tinkerpop/Gremlin 这两条完全不同的路,这是一个变化很快的时代,计算机又是一个更强调 de facto 标准的行业,GQL 并不容易。(打脸)

参考文献

  • [Angles2008] Renzo Angles and Claudio Gutierrez, “Survey of Graph Database Models”, ACM Computing Surveys, Vol. 40, February 2008
  • [PG2010] Marko A. Rodriguez and Peter Neubauer, “The Graph Traversal Pattern”, April 2010
  • [Wood2012] Peter Wood, “Query Languages for Graph Databases”, SIGMOD Record 2012
  • [Cypher2018] Nadime Francis, Alastair Green, Paolo Guagliardo, Leonid Libkin, Tobias Lindaaker, Victor Marsault, Stefan Plantikow, Mats Rydberg, Petra Selmer, and Andrés Taylor, “Cypher: An Evolving Query Language for Property Graphs”, SIGMOD 2018
  • [GCORE2018] Renzo Angles, Marcelo Arenas, Pablo Barceló, Peter Boncz, George Fletcher, Claudio Gutierrez, Tobias Lindaaker, Marcus Paradies, Stefan Plantikow, Juan Sequeda, Oskar van Rest, and Hannes Voigt, “G-CORE A Core for Future Graph Query Languages”, SIGMOD 2018
  • [Cypher9] openCypher, “Cypher Query Language Reference”, Version 9, Nov. 2017
  • Renzo2017 Renzo Angles, Marcelo Arenas, Pablo Barceló, Aidan Hogan, Juan Reutter, and Domagoj Vrgoč. 2017. Foundations of Modern Query Languages for Graph Databases. ACM Comput. Surv. 50, 5, Article 68 (November 2017), 40 pages.
  • Kleppmann2017 Kleppmann, Martin: Designing Data-Intensive Applications. Beijing : O'Reilly, 2017. - ISBN 978-1-4493-7332-0
  • [Tobias2018] Title: An overview of the recent history of Graph Query Languages. Authors Tobias Lindaaker, U.S. National Expert.Date: 2018-05-14
  • Deutsh2020 Alin Deutsch, Yu Xu, Mingxi Wu, and Victor E. Lee. 2020. Aggregation Support for Modern Graph Analytics in TigerGraph. In Proceedings of the 2020 ACM SIGMOD International Conference on Management of Data (SIGMOD '20). Association for Computing Machinery, New York, NY, USA, 377–392.
  • Grzegorz2010 Grzegorz Malewicz, Matthew H. Austern, Aart J.C Bik, James C. Dehnert, Ilan Horn, Naty Leiser, and Grzegorz Czajkowski. 2010. Pregel: a system for large-scale graph processing. In Proceedings of the 2010 ACM SIGMOD International Conference on Management of data (SIGMOD '10). Association for Computing Machinery, New York, NY, USA, 135–146.
  • Nadime2018 Nadime Francis, Alastair Green, Paolo Guagliardo, Leonid Libkin, Tobias Lindaaker, Victor Marsault, Stefan Plantikow, Mats Rydberg, Petra Selmer, and Andrés Taylor. 2018. Cypher: An Evolving Query Language for Property Graphs. In Proceedings of the 2018 International Conference on Management of Data (SIGMOD '18). Association for Computing Machinery, New York, NY, USA, 1433–1445.
  • databricks2020https://docs.databricks.com/spark/latest/graph-analysis/graphframes/user-guide-scala.html#motif-finding

交流图数据库技术?加入 Nebula 交流群请先填写下你的 Nebulae 名片,Nebula 小助手会拉你进群~~

查看大厂实践案例?Follow Nebula 公众号NebulaGraphCommunity 回复「PPT」掌握【美团的图数据库系统】、【微众银行的数据治理方案】以及其他大厂的风控、知识图谱实践。

推荐阅读

查看原文

赞 0 收藏 0 评论 0

NebulaGraph 发布了文章 · 4月1日

集群通信:从心跳说起

本文首发 Nebula Graph 官网:https://nebula-graph.com.cn/posts/cluster-communication-heartbeat/

在用户使用 Nebula Graph 的过程中,经常会遇到各种问题,通常我们都会建议先通过 show hosts 查看集群状态。可以说,整个 Nebula Graph 的集群状态都是靠心跳机制来构建的。本文将从心跳说起,帮助你了解 Nebula Graph 集群各个节点之间通信的机制。

什么是心跳?有什么作用?

metad storaged graphd 通信

Nebula Graph 集群一般包含三种节点,graphd 作为查询节点,storaged 作为存储节点,metad 作为元信息节点。本文说的心跳,主要是指 graphd 和 storaged 定期向 metad 上报信息的这个心跳,借助心跳,整个集群完成了以下功能。(相关参数是 heartbeat_interval_secs

在 Nebula Graph 中经常提及的 raft 心跳则是用于拥有同一个 partition 的多个 storaged 之间的心跳,和本文提的心跳并不相同。

1. 服务发现

当我们启动一个 Nebula Graph 集群时,需要在对应的配置文件中填写 meta_server_addrs。graphd 和 storaged 在启动之后,就会通过这个 meta_server_addrs 地址,向对应的 metad 发送心跳。通常来说,graphd 和 storaged 在连接上 metad 前是无法对外进行服务的。当 metad 收到心跳后,会保存相关信息(见下文第 2 点),此时就能够通过 show hosts 看到对应的 storaged 节点,在 2.x 版本中,也能够通过 show hosts graph 看到 graphd 节点。

2. 上报节点信息

在 metad 收到心跳时,会将心跳中的 ip、port、节点类型、心跳时间等等信息保存,以供后续使用(见下文)。

除此以外 storaged 在自身 leader 数量变化的时候也会上报 leader 信息,在 show hosts 中看到的 Leader count 和 Leader distribution 就是通过心跳汇报的。

3. 更新元信息

当客户通过 console 或者各种客户端,对集群的元信息进行更改之后(例如 create/drop spacecreate/alter/drop tag/edgeupdate configs 等等),通常在几秒之内,整个集群就都会更新元数据。

每次 graphd 和 storaged 在心跳的响应中会包含一个 last_update_time,这个时间是由 metad 返回给各个节点的,用于告知 metad 自身最后一次更新元信息的时间。当 graphd 或者 storaged 发现 metad 的元信息有更新,就会向 metad 获取相应信息(例如 space 信息、schema 信息、配置更改等等)。

我们以创建一个 tag 为例,如果在 graphd/storaged 获取到新创建的这个 tag 信息之前,我们无法插入这个 tag 数据(会报类似 No schema found 这样的错误)。而当通过心跳获取到对应信息并保存至本地缓存后,就能够正常写入数据了。

心跳上报的信息有什么用?

  • how hostsshow parts 这类命令都是通过 metad 中保存的各个节点心跳信息,组合显示出来的。
  • balance databalance leader 等运维命令,需要通过获取当前集群内哪些 storaged 节点是在线状态,实际也是通过 metad 判断最近一次心跳时间是否在阈值之内。
  • create space,当用户创建一个 space 时,metad 也需要获取 storaged 的状态,将这个 space 的各个 partition 分配到在线的 storaged 中。
以用户容易遇到的问题为例:假如我们启动一个 storaged 后,关掉并修改端口号,然后再启动 storaged。如果这个过程足够快,那么通过 show hosts 能看到两个在线的 storaged。此时,如果新建一个 space,例如 CREATE space test(partition_num=10, replica_factor=1),这个 test space 就会分布在前后启动的两个 storage 上。但如果等到在 show hosts 中看到其中一个离线后,再执行 CREATE space test(partition_num=10, replica_factor=1),即便离线的 storaged 再启动,也只有一个 storaged 拥有这个 space(创建 test space 时 online 的那个 storaged)。

上线

掉线

心跳的演变历史

在 18-19 年的时候,当时的心跳机制没有这么完善。一方面,无论元信息是否更改,都会从 metad 获取最新的元信息。而通常来说,元信息改动不会很频繁,定期获取元信息有一定的资源浪费。另一方面,想要将一个 storaged 节点加入和移除都是通过类似 add/delete hosts 这样的命令,采取的是类似白名单的机制。对于其他没有认证过的节点,都无法对外服务,这样做固然也有一些优势,带来的最大问题就是不够友好。

因此,在 19 年底开始,我们对心跳做了一系列的改动,特别鸣谢社区用户 @zhanggguoqing。经过一段时间的验证踩坑后,基本就形成了现在的形式。

额外的补充

有关心跳还有一个涉及到的问题就是 cluster.id 这个文件。它实际是为了防止 storaged 与错误的 metad 通信,大致原理如下:

  • 首先,metad 在启动的时候会根据 meta_server_addrs 这个参数,生成一个 hash 值并保存在本地 kv 中。
  • storaged 在启动的时候会尝试从 cluster.id 这个文件中获取对应 metad 的 hash 值,并附送在心跳中发送(如果文件不存在,则第一次使用 0 代替。收到心跳响应时,将 metad 的 hash 值保存在 cluster.id 这个文件中,后续一直使用该值)。
  • 在心跳处理中,metad 会比较本地 hash 值和来自 storaged 心跳请求中的 hash 值,如果不匹配则拒绝。此时,storaged 是无法对外服务的,也就是 Reject wrong cluster host 这个日志的由来。

以上就是心跳机制大致的介绍,感兴趣的你可以参考下源码实现,GitHub 传送门:https://github.com/vesoft-inc/nebula-graph

推荐阅读

查看原文

赞 0 收藏 0 评论 0

NebulaGraph 发布了文章 · 3月19日

Spark Connector Writer 原理与实践

nebula-spark-connector-reader

《Spark Connector Reader 原理与实践》中我们提过 Spark Connector 是一个 Spark 的数据连接器,可以通过该连接器进行外部数据系统的读写操作,Spark Connector 包含两部分,分别是 Reader 和 Writer,而本文主要讲述如何利用 Spark Connector 进行 Nebula Graph 数据的写入。

Spark Connector Writer 原理

Spark SQL 允许用户自定义数据源,支持对外部数据源进行扩展。

Nebula 的 Spark Connector 单条数据写入是基于 DatasourceV2 实现的,需要以下几个步骤:

  1. 继承 WriteSupport 并重写 createWriter,创建自定义的 DataSourceWriter
  2. 继承 DataSourceWriter 创建 NebulaDataSourceVertexWriter 类和 NebulaDataSourceEdgeWriter 类,重写 createWriterFactory 方法并返回自定义的 DataWriterFactory,重写 commit 方法,用来提交整个事务。重写 abort 方法,用来做事务回滚。Nebula Graph 1.x 不支持事务操作,故该实现中 commitabort 无实质性操作。
  3. 继承 DataWriterFactory 创建 NebulaVertexWriterFactory 类和 NebulaEdgeWriterFactory 类,重写 createWriter 方法返回自定义的 DataWriter
  4. 继承 DataWriter 创建 NebulaVertexWriter 类和 NebulaEdgeWriter 类,重写 write 方法,用来将数据写出,重写 commit 方法用来提交事务,重写 abort 方法用来做事务回滚 ,同样 DataWriter 中的 commit 方法和 abort 方法无实质性操作。

Nebula 的 Spark Connector Writer 的实现类图如下:

nebula-spark-connector-writer

具体写入逻辑在 NebulaVertexWriter 和 NebulaEdgeWriter 的 write 方法中,一次写入的逻辑如下:

  1. 创建客户端,连接 Nebula 的 graphd 服务;
  2. 数据写入前先指定 graphSpace;
  3. 构造 Nebula 的数据写入 statement;
  4. 提交 statement,执行写入操作;
  5. 定义回调函数接收写入操作执行结果。

Nebula 的 Spark Connector 的批量数据写入与 Exchange 工具类似,是通过对 DataFrame 进行 map 操作批量数据累计提交实现的。

Spark Connector Writer 实践

Spark Connector 的 Writer 功能提供了两类接口供用户编程进行数据写入。写入的数据源为 DataFrame,Spark Writer 提供了单条写入批量写入两类接口。

拉取 GitHub 上 Spark Connector 代码:

git clone -b v1.0 https://github.com/vesoft-inc/nebula-java.git
cd nebula-java/tools/nebula-spark
mvn clean compile package install -Dgpg.skip -Dmaven.javadoc.skip=true

将编译打成的包 copy 到本地 maven 库。

应用示例如下:

  1. 在 mvn 项目的 pom 文件中加入 nebula-spark 依赖
<dependency>
  <groupId>com.vesoft</groupId>
  <artifactId>nebula-spark</artifactId>
  <version>1.0.1</version>
</dependency>
  1. 在 Spark 程序中将 DataFrame 数据写入 Nebula
  • 2.1 逐条写入 Nebula:
// 构造点和边数据的 DataFrame ,示例数据在 nebula-java/examples/src/main/resources 目录下
val vertexDF = spark.read.json("examples/src/main/resources/vertex")
    vertexDF.show()
val edgeDF = spark.read.json("examples/src/main/resources/edge")
        edgeDF.show()

// 写入点
vertexDF.write
  .nebula("127.0.0.1:3699", "nb", "100")
  .writeVertices("player", "vertexId", "hash")
  
// 写入边
edgeDF.write
    .nebula("127.0.0.1:3699", "nb", "100")
  .wirteEdges("follow", "source", "target")

配置说明:

    • nebula(address: String, space: String, partitionNum: String)

      • address:可以配置多个地址,以英文逗号分割,如“ip1:3699,ip2:3699”
      • space: Nebula 的 graphSpace
      • partitionNum:创建 space 时指定的 Nebula 中的 partitionNum,未指定则默认为 100
    • writeVertices(tag: String, vertexFiled: String, policy: String = "")

      • tag:Nebula 中点的 tag
      • vertexFiled:Dataframe 中可作为 Nebula 点 ID 的列,如 DataFrame 的列为 a,b,c,如果把 a 列作为点的 ID 列,则该参数设置为 a
      • policy:若 DataFrame 中 vertexFiled 列的数据类型非数值型,则需要配置 Nebula 中 VID 的映射策略
    • writeEdges(edge: String, srcVertexField: String, dstVertexField: String, policy: String = "")

      • edge:Nebula 中边的 edge
      • srcVertexField:DataFrame 中可作为源点的列
      • dstVertexField:DataFrame 中可作为边目标点的列
      • policy:若 DataFrame 中 srcVertexField 列或 dstVertexField 列的数据类型非数值型,则需要配置 Nebula 中 edge ID 的映射策略
    • 2.2 批量写入 Nebula
    // 构造点和边数据的 DataFrame ,示例数据在 nebula-java/examples/src/main/resources 目录下
    val vertexDF = spark.read.json("examples/src/main/resources/vertex")
        vertexDF.show()
    val edgeDF = spark.read.json("examples/src/main/resources/edge")
            edgeDF.show()
    
    // 批量写入点
    new NebulaBatchWriterUtils()
          .batchInsert("127.0.0.1:3699", "nb", 2000)
          .batchToNebulaVertex(vertexDF, "player", "vertexId")
      
    // 批量写入边
    new NebulaBatchWriterUtils()
          .batchInsert("127.0.0.1:3699", "nb", 2000)
          .batchToNebulaEdge(edgeDF, "follow", "source", "target")
    

    配置说明:

    • batchInsert(address: String, space: String, batch: Int = 2000)

      • address:可以配置多个地址,以英文逗号分割,如“ip1:3699,ip2:3699”
      • space:Nebula 的 graphSpace
      • batch:批量写入时一批次的数据量,可不配置,默认为 2000
    • batchToNebulaVertex(data: DataFrame, tag: String, vertexField: String, policy: String = "")

      • data:待写入 Nebula 的 DataFrame 数据
      • tag:Nebula 中点的 tag
      • vertexField:Dataframe 中可作为 Nebula 点 ID 的列
      • policy:Nebula 中 VID 的映射策略,当 vertexField 列的值为数值时可不配置
    • batchToNebulaEdge(data: DataFrame,  edge: String, srcVertexField: String, dstVertexField: String, rankField: String = "",  policy: String = "")

      • data:待写入 Nebula 的 DataFrame 数据
      • edge:Nebula 中边的 edge
      • srcVertexField:DataFrame 中可作为源点的列
      • dstVertexField:DataFrame 中可作为边目标点的列
      • rankField:DataFrame 中可作为边 rank 值的列,可不配置
      • policy:edge 中点的映射策略,当 srcVertexField 和 dstVertexField 列的值为数值时可不配置

    至此,Nebula Spark Connector Writer 讲解完毕,欢迎前往 GitHub:https://github.com/vesoft-inc/nebula-java/tree/v1.0/tools/nebula-spark 试用。

    喜欢这篇文章?来来来,给我们的 GitHub 点个 star 表鼓励啦~~ 🙇‍♂️🙇‍♀️ [手动跪谢]

    交流图数据库技术?交个朋友,Nebula Graph 官方小助手微信:NebulaGraphbot 拉你进交流群~~

    查看原文

    赞 0 收藏 0 评论 0

    NebulaGraph 发布了文章 · 3月10日

    Nebula Storage 2.0 存储格式

    storage-format-in-nebula-graph-2.0

    随着 2.0 各版本的陆续发布,Nebula Graph 迎来了一系列的改动,在存储方面,影响最大的改动就是底层编码格式进行了修改。Nebula Graph 的底层存储是基于 KV 保存在 RocksDB 中,本文将介绍新老编码格式的差异,以及为什么要修改存储格式等一系列问题。

    1.0 版本的格式

    我们先简单回顾下 1.0 版本的编码格式,不熟悉的可以参考这篇博客《Nebula 架构剖析系列(一)图数据库的存储设计》。由于在 1.0 版本中,点的 ID 只能够用整型来表示,所以底层所有 VertexID 都是以 int64 来保存的。

    • 点的格式

    Nebula Graph 1.0 点的格式

    • 边的格式

    Nebula Graph 1.0 边的格式

    给定任何一个 VertexID,经过 hash,可以得到对应的 PartID,因此对于一个点和这个点的所有边(边用起点计算 hash),都会映射到同一个分片中。需要指出的是,在 1.0 版本中,点和边的第一个字节的 Type 是相同的。也就是说,对于一个点而言,它的所有 tag 并没有在物理上连续保存,比如可能是如下保存的。对于这个 src 这个点的三个 tag(tag1 tag2 tag3),实际上可能会被其他边隔开。

    rocksdb key 存储

    这个格式能够满足 1.0 绝大多数接口的需要,比如 fetchgo 都只需要指定对应前缀,就能获取对应数据。

    2.0 版本的格式

    在 GA 之前发布的版本,底层存储格式其实和 1.0 是基本相同的。如果 VertexID 是整型,和 1.0 格式完全一致。而如果 VertexID 类型支持 string,则从占用 8 个字节的 int64 改成了固定长度的 FIXED_STRING,长度需要用户在 create space 时候指定长度。对于不足的长度系统自动使用 \0 补齐,而超过指定长度的 VertexID 会直接报错。

    在 GA 版本中,我们对底层存储格式进行了若干改动,因此这次版本升级时需要通过升级工具,将原有格式的数据转换为新格式的数据。如下是在 2.0 GA 版本中采用的存储格式。

    2.0 版本存储格式

    • 点的格式

    Nebula Graph 2.0 点的格式

    • 边的格式

    Nebula Graph 2.0 边的格式

    和 1.0 存储格式对比

    点格式版本对比

    点格式版本对比

    其中有几个比较大的改动:

    1. VertexID 的长度如前文所说,从固定的 8 字节,修改为 n 个字节。VertexID 类型为整型时,n 为 8,VertexID 类型为 string 类型时,n 为指定长度。
    2. 点去掉了 1.0 的时间戳。边将 1.0 时间戳改为了一个字节的占位符。
    3. 对于点和边的第一个字节,不再使用同一个 Type,在物理上点和边进行了分离。

    这些改动主要是基于以下几点进行考虑的:

    1. VertexID 改变主要是为了支持 string ID 同时兼容 1.0 版本 int ID。在 storage 中,把 VertexID 都处理为 bytes,只在返回结果时根据 space 的设置不同,返回相应类型的 VertexID。

      为什么 string ID 要使用 FIXED_STRING ? 如果不使用固定长度,则无法使用前缀进行扫描。通过长度不足补齐,使得所有点之间和边之间的各个前缀长度相同,从而进行相应的前缀查询。
    2. 去掉时间戳主要是因为保存多版本数据会影响性能,另外一段时间内暂时不考虑做 MVCC 的相关工作。

      在边里面还保留一个字节的占位符,主要是留给 TOSS(transaction on storage side)使用。主要用于标识一条边的出边和入边是否完整插入了,这里不详细介绍,后续会有其他文章进行详尽的分析。
    3. 点和边分离的好处主要是能够方便快速拿某个点的所有 tag(在Cypher 的 MATCH 语句中大量使用)。如果按原先同一 Type + VertexID 前缀扫描,由于点边可能掺杂在一起,会极大影响性能。而 Type 分离之后,按 VertexType + VertexID 前缀扫描,可以快速获取所有 tag。

      在 1.0 版本中,由于没有取某个点的所有 tag 的需求,因此点和边可以按同一个前缀保存。不过在代码层面,还是有不小影响,例如 fetch 接口在 1.0 是按 VertexID 的前缀去扫描的,对于超级大点来说取 tag 性能比较差。另外如果使用 storage 提供的 scan 接口,想要获取全图的所有点,实际是扫描了整个 RocksDB。

    除了点和边的格式相关改动之外,索引的格式其实也有所改变。

    一方面是 2.0 支持 NULL 后,索引也需要能够表示对应的语义。另一方面是在 1.0 的版本中,对于索引中 string 的字段的处理,实际是按变长 string 处理。因此在 LOOKUP 语句中只要使用了带 string 字段的索引,就只能使用等值查询。而在 2.0 的版本中,索引的 string 字段和数据中的 VertexID 一样,使用固定长度的 FIXED_STRING,LOOKUP 语句中带 string 字段的索引能够使用范围查询,例如 LOOKUP ON index1 WHERE col > "aaa"。有关索引部分的功能和修改,后续也会再有其他文章介绍。

    以上,为本次 Nebula Storage 2.0 存储格式讲解。

    喜欢这篇文章?来来来,给我们的 GitHub 点个 star 表鼓励啦~~ 🙇‍♂️🙇‍♀️ [手动跪谢]

    交流图数据库技术?交个朋友,Nebula Graph 官方小助手微信:NebulaGraphbot 拉你进交流群~~

    推荐阅读

    查看原文

    赞 0 收藏 0 评论 0

    NebulaGraph 发布了文章 · 3月3日

    Kubernetes 部署 Nebula 图数据库集群

    本文首发于 Nebula Graph 官网:https://nebula-graph.com.cn/posts/how-to-deploy-nebula-graph-in-kubernetes/

    Kubernetes 是什么

    Kubernetes 是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化的应用简单并且高效,Kubernetes 提供了应用部署,规划,更新,维护的一种机制。

    Kubernetes 在设计结构上定义了一系列的构建模块,其目的是为了提供一个可以部署、维护和扩展应用程序的机制,组成 Kubernetes 的组件设计理念为松耦合可扩展的,这样可以使之满足多种不同的工作负载。可扩展性在很大程度上由 Kubernetes API 提供,此 API 主要被作为扩展的内部组件以及 Kubernetes 上运行的容器来使用。

    k8s-architecture

    Kubernetes 主要由以下几个核心组件组成:

    • etcd 保存了整个集群的状态
    • kube-apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制
    • kube-controller-manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等
    • kube-scheduler 负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上
    • kubelet 负责维护容器的生命周期,同时也负责 Volume和网络的管理
    • kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡

    Kubernetes 和数据库

    数据库容器化是最近的一大热点,那么 Kubernetes 能为数据库带来什么好处呢?

    • 故障恢复:Kubernetes 提供故障恢复的功能,数据库应用如果宕掉,Kubernetes 可以将其自动重启,或者将数据库实例迁移到集群中其他节点上
    • 存储管理:Kubernetes 提供了丰富的存储接入方案,数据库应用能透明地使用不同类型的存储系统
    • 负载均衡:Kubernetes Service 提供负载均衡功能,能将外部访问均衡到不同的数据库实例副本上
    • 水平拓展:Kubernetes 可以根据当前数据库集群的资源利用率情况,缩放副本数目,从而提升资源的利用率

    目前很多数据库,如:MySQL,MongoDB 和 TiDB 在 Kubernetes 集群中都能运行良好。

    Nebula Graph 在 Kubernetes 中的实践

    Nebula Graph 是一个分布式的开源图数据库,主要组件有:Query Engine 的 graphd,数据存储的 storaged,和元数据的 meted。在 Kubernetes 实践过程中,它主要给图数据库 Nebula Graph 带来了以下的好处:

    • Kubernetes 能均衡 nebula graphd,metad 和 storaged 不同副本之间的负载。graphd,metad 和 storaged 可以通过 Kubernetes 的域名服务自动发现彼此。
    • 通过 StorageClass,PVC 和 PV 可以屏蔽底层存储细节,无论使用本地卷还是云盘,Kubernetes 均可以透明对接。
    • 通过 Kubernetes 可以在数秒内成功部署一套 Nebula 集群,Kubernetes 也可以无感知地实现 Nebula 集群的升级。
    • Nebula 集群通过 Kubernetes 可以做到自我恢复,单体副本 crash,Kubernetes 可以重新将其拉起,无需运维人员介入。
    • Kubernetes 可以根据当前 Nebula 集群的资源利用率情况弹性伸缩集群规模,从而提升集群的性能。

    下面来讲解下具体的实践内容。

    集群部署

    硬件和软件要求

    这里主要罗列下本文部署涉及到的机器、操作系统参数

    • 操作系统使用的 CentOS-7.6.1810 x86_64
    • 虚拟机配置

      • 4 CPU
      • 8G 内存
      • 50G 系统盘
      • 50G 数据盘 A
      • 50G 数据盘 B
    • Kubernetes 集群版本 v1.14+
    • Nebula 版本为 v2.0.0-rc1
    • 使用本地 PV 作为数据存储
    • CoreDNS 版本 1.6.0+

    K8s 集群规划

    以下为集群清单

    服务器 IPnebula 实例role
    192.168.0.1master
    192.168.0.2graphd, metad-0, storaged-0node
    192.168.0.3graphd, metad-1, storaged-1node
    192.168.0.4graphd, metad-2, storaged-2node

    K8s 待部署组件

    • 安装 Helm3
    • 准备本地磁盘,并安装本地卷插件
    • 安装 nebula 集群

    安装 Helm3

    Helm 是 Kubernetes 集群上的包管理工,使用 Helm 可以极大地降低使用 Kubernetes 部署应用的门槛。本文不做 Helm 详细介绍,有兴趣的小伙伴可自行阅读《Helm 入门指南》

    下载安装 Helm

    使用下面命令在终端执行即可安装 Helm

    $ wget https://get.helm.sh/helm-v3.5.2-linux-amd64.tar.gz
    $ tar -zxvf helm/helm-v3.5.2-linux-amd64.tgz
    $ mv linux-amd64/helm /usr/bin/helm

    查看 Helm 版本

    执行 helm version 命令即可查看对应的 Helm 版本,以文本为例,以下为输出结果:

    version.BuildInfo{Version:"v3.5.2", GitCommit:"167aac70832d3a384f65f9745335e9fb40169dc2", GitTreeState:"dirty", GoVersion:"go1.15.7"}

    设置本地磁盘

    在每台机器上做如下配置

    创建 mount 目录

    $ sudo mkdir -p /mnt/disks

    格式化数据盘

    $ sudo mkfs.ext4 /dev/diskA 
    $ sudo mkfs.ext4 /dev/diskB

    挂载数据盘

    $ DISKA_UUID=$(blkid -s UUID -o value /dev/diskA) 
    $ DISKB_UUID=$(blkid -s UUID -o value /dev/diskB) 
    $ sudo mkdir /mnt/disks/$DISKA_UUID
    $ sudo mkdir /mnt/disks/$DISKB_UUID
    $ sudo mount -t ext4 /dev/diskA /mnt/disks/$DISKA_UUID
    $ sudo mount -t ext4 /dev/diskB /mnt/disks/$DISKB_UUID
    
    $ echo UUID=`sudo blkid -s UUID -o value /dev/diskA` /mnt/disks/$DISKA_UUID ext4 defaults 0 2 | sudo tee -a /etc/fstab
    $ echo UUID=`sudo blkid -s UUID -o value /dev/diskB` /mnt/disks/$DISKB_UUID ext4 defaults 0 2 | sudo tee -a /etc/fstab

    部署本地卷插件

    $ curl https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner/archive/v2.4.0.zip
    $ unzip v2.4.0.zip

    修改 v2.4.0/helm/provisioner/values.yaml classes 部分:

    • hostDir: /mnt/fast-disks 改成 hostDir: /mnt/disks# storageClass: true 改成 storageClass: true。然后执行:
    $ helm install local-static-provisioner --namespace default sig-storage-local-static-provisioner/helm/provisioner
    # 查看 local-static-provisioner 部署情况
    $ helm list
    NAME                        NAMESPACE    REVISION    UPDATED                                  STATUS      CHART                APP VERSION
    local-volume-provisioner    default      1           2021-02-10 11:06:34.3540341 +0800 CST    deployed    provisioner-2.4.0    2.4.0 

    部署 nebula 集群

    下载 nebula helm chart

    # 下载 nebula chart
    $ helm repo add nebula-charts https://vesoft-inc.github.io/nebula-docker-compose
    $ helm pull nebula-charts/nebula
    $ tar -zxvf nebula-v2.0.0.tgz

    设置 Kubernetes node节点

    下面是 Kubernetes 节点列表,我们需要设置 node 节点的调度标签。可以将 _192.168.0.2_,_192.168.0.3_,_192.168.0.4_ 打上 nebula: "cloud" 的标签。

    服务器 IPkubernetes rolesnodeName
    192.168.0.1master192.168.0.1
    192.168.0.2node192.168.0.2
    192.168.0.3node192.168.0.3
    192.168.0.4node192.168.0.4

    具体操作如下:

    $ kubectl  label node 192.168.0.2 nebula="cloud" --overwrite 
    $ kubectl  label node 192.168.0.3 nebula="cloud" --overwrite
    $ kubectl  label node 192.168.0.4 nebula="cloud" --overwrite

    调整 values 默认值

    nebula helm chart 目录如下:

    nebula
    ├── Chart.yaml
    ├── README.md
    ├── templates
    │   ├── configmap.yaml
    │   ├── deployment.yaml
    │   ├── _helpers.tpl
    │   ├── NOTES.txt
    │   ├── pdb.yaml
    │   ├── serviceaccount.yaml
    │   ├── service.yaml
    │   └── statefulset.yaml
    └── values.yaml
    
    1 directory, 11 files

    可以根据运行环境需求修改 charts/nebula/values.yaml  里面的默认值

    通过 helm 安装 nebula

    $ helm install nebula charts/nebula 
    # 查看部署状态
    $ helm status nebula
    NAME: nebula
    LAST DEPLOYED: Fri Feb 19 12:58:16 2021
    NAMESPACE: default
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    NOTES:
    Nebula Graph Cluster installed!
    
    1. Watch all containers come up.
      $ kubectl get pods --namespace=default -l app.kubernetes.io=nebula -w
    # 查看 K8s 集群上 nebula 部署情况
    $ kubectl get pods --namespace=default -l app.kubernetes.io=nebula
    NAME                             READY   STATUS    RESTARTS   AGE
    nebula-graphd-676cfcf797-4q7mk   1/1     Running   0          6m
    nebula-graphd-676cfcf797-whwqp   1/1     Running   0          6m
    nebula-graphd-676cfcf797-zn5l6   1/1     Running   0          6m
    nebula-metad-0                   1/1     Running   0          6m
    nebula-metad-1                   1/1     Running   0          6m
    nebula-metad-2                   1/1     Running   0          6m
    nebula-storaged-0                1/1     Running   0          6m
    nebula-storaged-1                1/1     Running   0          6m
    nebula-storaged-2                1/1     Running   0          6m
    

    访问 nebula 集群:

    $ kubectl get service nebula-graphd
    NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                                          AGE
    nebula-graphd   NodePort   10.105.47.116   <none>        9669:31646/TCP,19669:30554/TCP,19670:32386/TCP   22m
    
    # 使用 nebula-console 测试
    $ docker run --rm -ti --entrypoint=/bin/sh vesoft/nebula-console:v2-nightly
    # 通过 NodePort 连接到 graphd 组件
    / $ nebula-console -addr 192.168.0.4 -port 31646 -u root -p vesoft
    2021/02/19 05:04:55 [INFO] connection pool is initialized successfully
    
    Welcome to Nebula Graph v2.0.0-rc1!
    
    (root@nebula) [(none)]> show hosts;
    +---------------------------------------------------------------+------+----------+--------------+----------------------+------------------------+
    | Host                                                          | Port | Status   | Leader count | Leader distribution  | Partition distribution |
    +---------------------------------------------------------------+------+----------+--------------+----------------------+------------------------+
    | "nebula-storaged-0.nebula-storaged.default.svc.cluster.local" | 9779 | "ONLINE" | 0            | "No valid partition" | "No valid partition"   |
    +---------------------------------------------------------------+------+----------+--------------+----------------------+------------------------+
    | "nebula-storaged-1.nebula-storaged.default.svc.cluster.local" | 9779 | "ONLINE" | 0            | "No valid partition" | "No valid partition"   |
    +---------------------------------------------------------------+------+----------+--------------+----------------------+------------------------+
    | "nebula-storaged-2.nebula-storaged.default.svc.cluster.local" | 9779 | "ONLINE" | 0            | "No valid partition" | "No valid partition"   |
    +---------------------------------------------------------------+------+----------+--------------+----------------------+------------------------+
    | "Total"                                                       |      |          | 0            |                      |                        |
    +---------------------------------------------------------------+------+----------+--------------+----------------------+------------------------+
    Got 4 rows (time spent 2608/4258 us)
    

    FAQ

    如何搭建一套 Kubernetes 集群?

    搭建高可用的 Kubernetes 可以参考社区文档:https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/

    如何调整 nebula 集群的部署参数?

    在使用 helm install 时,使用 --set 可以设置部署参数,从而覆盖掉 helm chart 中 values.yaml 中的变量。参考文档:https://helm.sh/docs/intro/using_helm/

    能否兼容 nebula v1.0.0+ 集群部署?

    v1.0.0+ 不支持内部域名解析,需要修改 charts/nebula/values.yaml,配置如下:

    hostNetwork: true
    metadEndpoints: []
      - 192.168.0.2:9559
      - 192.168.0.3:9559
      - 192.168.0.4:9559
    如何在 K8s 集群外部访问 nebula 内部组件?

    本文使用 NodePort 的方式访问 graphd 组件,其他的访问方式还有 hostPort、hostNetwork、Ingress、LoadBalancer,需要您根据实际的部署环境来定制。

    如何查看 nebula 集群状况?

    使用 kubectl get pods --namespace=default -l app.kubernetes.io=nebula命令,或者直接在 Kubernetes dashboard 上查看 nebula 集群的运行状况。

    如何使用其他类型的存储?

    参考文档:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

    推荐阅读

    查看原文

    赞 0 收藏 0 评论 0

    NebulaGraph 发布了文章 · 2月2日

    Neo4j 导入 Nebula Graph 的实践总结

    摘要: 主要介绍如何通过官方 ETL 工具 Exchange 将业务线上数据从 Neo4j 直接导入到 Nebula Graph 以及在导入过程中遇到的问题和优化方法。

    本文首发于 Nebula 论坛:https://discuss.nebula-graph.com.cn/t/topic/2044

    Neo4j 导入 Nebula Graph 的实践总结

    1 背景

    随着业务数据量不断增长,业务对图数据库在线数据实时更新写入和查询的效率要求也不断增加。Neo4j 存在明显性能不足,Neo4j 社区开源版本只支持单机部署,扩展能力存在比较大的问题,无法满足读写性能的线性扩展以及读写分离的业务需求,并且开源版本 Neo4j 对点和边的总数据量也有限制;而 Neo4j 企业版因果集群也存在单机主节点 Cypher 实时写入的性能瓶颈。

    相比于 Neo4j,Nebula Graph 最大的特色便是采用 shared-nothing 分布式的架构,无单主写入瓶颈问题,读写支持线性扩展,擅长处理千亿节点、万亿条边的超大规模数据集。

    本文主要介绍如何通过官方 ETL 工具 Exchange 将业务线上数据从 Neo4j 直接导入到 Nebula Graph 以及在导入过程中遇到的问题和优化方法。其中绝大部分问题都已经通过论坛发帖的方式得到社区的支持和解决,本文会结合问题进行逐一列举。

    2 部署环境

    系统环境:

    • CPU name:Intel(R) Xeon(R) Silver 4114 CPU @ 2.20GHz
    • CPU Cores:40
    • Memory Size:376 GB
    • Disk:HDD
    • System:CentOS Linux release 7.4.1708 (Core)

    软件环境:

    • Neo4j:3.4 版本,五节点因果集群
    • Nebula Graph:

    • Exchange:nebula-java v1.1.0 源码编译 jar 包
    • 数仓环境:

      • hadoop-2.7.4
      • spark-2.3.1

    注意:单台机器部署 Nebula 多节点的端口分配:每个 storage 还会将用户配置的端口号 + 1的端口作为内部使用。请参考论坛帖子 nebula从neo4j导入数据出现Get UUID Failed错误

    3 全量 & 增量数据导入

    3.1 全量导入

    根据 Neo4j 点和边的属性信息创建 Nebula Graph 的 Tag 和 Edge 结构,这里需要注意一点,业务可能会根据不同需求只在部分点和边上增加 Neo4j 点和边的属性信息,其他点和边对应的属性为 NULL,所以需要先跟业务明确一下点和边的全部属性信息,避免遗漏属性。Nebula Graph 的 Schema 信息类似 MySQL,支持 Create 和 Alter 添加属性,并且所有的 Tag 和 Edge 的元数据信息是一致的。

    1、Nebula Graph 创建 Tag 和 Edge

    # 示例
    # 创建图空间,10 个分区,3 个 storage 副本。
    CREATE SPACE test(partition_num=10,replica_factor=3);
    # 选择图空间 test
    USE test;
    # 创建标签 tagA
    CREATE TAG tagA(vid string, field-a0 string, field-a1 bool, field-a2 double);
    # 创建标签 tagB
    CREATE TAG tagB(vid string, field-b0 string, field-b1 bool, field-b2 double);
    # 创建边类型 edgeAB
    CREATE EDGE edgeAB(vid string, field-e0 string, field-e1 bool, field-e2 double);

    2、Exchange 导入配置文件

    • Exchange 配置目前不支持 bolt+routing 的方式连接neo4j,如果是因果集群,可以选择一个从节点进行 bolt 方式直连读取数据,减少集群压力。
    • 我们业务的 Neo4j 数据点和边的 vid 是 string 类型,Nebula v1.x 版本还不支持 string 直接当做 vid(v2.0支持),考虑到官方文档中的描述:“当点数量到达十亿级别时,用 hash 函数生成 vid 有一定的冲突概率。因此 Nebula Graph 提供 UUID 函数来避免大量点时的 vid 冲突。” 选择了uuid() 作为转化函数,但是导入效率要比 hash 低,而且 uuid() 在未来版本可能存在兼容问题。
    • partition: 是指 Exchange 从 Neo4j 拉取数据的分页个数。
    • batch: 是指批量插入 Nebula 的 batch 大小。
    {
      # Spark relation config
      spark: {
        app: {
          name: Spark Writer
        }
    
        driver: {
          cores: 1
          maxResultSize: 1G
        }
    
        cores {
          max: 16
        }
      }
    
      # Nebula Graph relation config
      nebula: {
        address:{
          graph:["xxx.xxx.xxx.xx:3699"]
          meta:["xxx.xxx.xxx.xx:45500"]
        }
        user: user
        pswd: password
        space: test
    
        connection {
          timeout: 3000
          retry: 3
        }
    
        execution {
          retry: 3
        }
    
        error: {
          max: 32
          output: /tmp/errors
        }
    
        rate: {
          limit: 1024
          timeout: 1000
        }
      }
      
      # Processing tags
      tags: [
        # Loading tag from neo4j
        {
          name: tagA
          type: {
            source: neo4j
            sink: client
          }
          server: "bolt://xxx.xxx.xxx.xxx:7687"
          user: neo4j
          password: neo4j
          exec: "match (n:tagA) where id(n) < 300000000 return n.vid as vid, n.field-a0 as field-a0, n.field-a1 as field-a1, n.field-a2 as field-a2 order by id(n)"
          fields: [vid, field-a0, field-a1, field-a2]
          nebula.fields: [vid, field-a0, field-a1, field-a2]
          vertex: {
            field: vid
            policy: "uuid"
          }
          partition: 10
          batch: 1000
          check_point_path: /tmp/test
        }
        # Loading tag from neo4j
        {
          name: tagB
          type: {
            source: neo4j
            sink: client
          }
          server: "bolt://xxx.xxx.xxx.xxx:7687"
          user: neo4j
          password: neo4j
          exec: "match (n:tagB) where id(n) < 300000000 return n.vid as vid, n.field-b0 as field-b0, n.field-b1 as field-b1, n.field-b2 as field-b2 order by id(n)"
          fields: [vid, field-b0, field-b1, field-b2]
          nebula.fields: [vid, field-b0, field-b1, field-b2]
          vertex: {
            field: vid
            policy: "uuid"
          }
          partition: 10
          batch: 1000
          check_point_path: /tmp/test
        }
      ]
    
      # Processing edges
      edges: [
       # Loading edges from neo4j
        {
          name: edgeAB
          type: {
            source: neo4j
            sink: client
          }
          server: "bolt://xxx.xxx.xxx.xxx:7687"
          user: neo4j
          password: neo4j
          exec: "match (a:tagA)-[r:edgeAB]->(b:tagB) where id(r) < 300000000 return n.vid as vid, n.field-e0 as field-e0, n.field-e1 as field-e1, n.field-e2 as field-e2 order by id(r)"
          fields: [vid, field-e0, field-e1, field-e2]
          nebula.fields: [vid, field-e0, field-e1, field-e2]
          source: {
            field: a.vid
            policy: "uuid"
          }
          target: {
            field: b.vid
            policy: "uuid"
          }
          partition: 10
          batch: 1000
          check_point_path: /tmp/test
        }
      ]
    }

    3、执行导入命令

    nohup spark-submit --class com.vesoft.nebula.tools.importer.Exchange --master "local" exchange-1.1.0.jar -c test.conf > test.log &

    4、查看导入 Nebula Graph 的数据量

    ./bin/db_dump --space=test --db_path=./data/storage/nebula/ --meta_server=127.0.0.1:45500 -limit 0 --mode=stat --tags=tagA,tagB --edges=edgeAB

    注意:Nebula 1.x 版本目前还只能用 db_dump 统计,2.0 会支持 nGQL 命令的方式统计数量。

    3.2 增量导入

    增量数据导入主要是通过 Neo4j 内部点和边的自增 id() 进行切割,在导入配置文件 exec 项执行 Neo4j Cypher 语句时增加 id() 范围限制,但前提是需要业务停掉删数据操作,因为增量导入时,如果之前的数据被删除后 Neo4j 会复用 id(),这会导致复用 id() 的增量数据导入时查询不到造成数据丢失。当然业务如果有条件支持 Neo4j Nebula 双写的话,增量导入就不会出现这种问题

    exec: "match (n:user) where id(n) >= 300000000 and id(n) < 400000000 return xxx order by id(n)"

    请参考论坛帖子 neo4j到nebula如何做增量导入

    3.3 导入问题及解决

    使用 Exchange 导入过程中遇到两个问题,及时的得到官方 @nicole 的支持和解决,具体请参考下面两个帖子:

    问题 1:Exchange 不支持「换行回车」等特殊字符的转义。如下 string 数据中带有回车,在拼接 insert 语句插入时会因为换行导致插入失败。

    Neo4j 导入 Nebula Graph 的实践总结

    PR:https://github.com/vesoft-inc... 已经合入 exchange v1.0 分支

    问题 2:Exchange 不支持属性为 NULL 的数据导入。前文 3.1 中提到,业务可能会根据不同需求为某些点和边增加属性,这时其他点和边属性则是 NULL,这样在使用 Exchange 导入时会报错。

    Neo4j 导入 Nebula Graph 的实践总结

    参考帖子 2 给出的修改建议解决:修改 com.vesoft.nebula.tools.importer.processor.Processor#extraValue,增加 NULL 类型的转化值。

    case NullType => {
      fieldTypeMap(field) match {
        case StringType => ""
        case IntegerType => 0
        case LongType => 0L
        case DoubleType => 0.0
        case BooleanType => false
      }
    }

    4 导入效率优化

    关于导入效率的优化,请参考下面两个帖子:

    优化 1:通过适当增加导入配置中的 partition 和 batch 值,提升导入效率。
    优化 2:如果是 string 类型做 vid 的话,1.x 版本尽量使用 hash() 函数转化,2.0 版本会支持 string id 类型;如果是int类型做vid的话,可以直接使用,不用转化效率更高。
    优化 3:官方建议 spark-submit 提交命令 master 配置改为 yarn-cluster, 若不使用 yarn,可配置成 spark://ip:port;我们是通过 spark-submit --master "local[16]" 的方式增加 spark 并发,导入效率比使用 "local" 提升 4 倍+,测试环境单机三节点 HDD 盘 IO 峰值能到 200-300 MB/s。但在指定 --master "local[16]" 并发导入时遇到 hadoop 缓存问题,采用增加 hdfs 配置 fs.hdfs.impl.disable.cache=true 后重启 hadoop 解决。具体请参考第二个帖子。

    5 总结

    使用 Exchange 从 Neo4j 导入 Nebula Graph 过程中遇到一些问题,通过积极与社区进行沟通得到了官方 @nicole 及其他小伙伴的快速响应和大力支持,这一点在 Neo4j 导入 Nebula Graph 的实践过程中起到了十分关键的作用,感谢社区的大力支持。期待支持 openCypher 的 Nebula Graph 2.0。

    6 参考链接

    1. https://nebula-graph.com.cn/posts/how-to-import-data-from-neo4j-to-nebula-graph/
    2. https://github.com/vesoft-inc/nebula-java/tree/v1.0
    3. https://docs.nebula-graph.com.cn/manual-CN/2.query-language/2.functions-and-operators/uuid/
    4. http://arganzheng.life/hadoop-filesystem-closed-exception.html

    推荐阅读

    查看原文

    赞 0 收藏 0 评论 0

    NebulaGraph 发布了文章 · 1月27日

    八大案例带你了解图数据库如何洞察数据间关联价值

    图数据库洞察数据间的关联价值

    大家好,我是吴敏。今天分享一个叫图数据库的技术产品。

    什么是图和图数据库

    图数据库洞察数据间的关联价值

    先来介绍一下什么是图和图数据库,所谓的图和平常认知的图片其实不是同一个概念,图(Graph)在计算机科学里面是一种数据结构,这种数据结构有三个比较主要的概念:点、边和属性。

    图数据库洞察数据间的关联价值

    通俗的说,图结构还有其他的叫法,比如:网络结构、拓扑结构,大致上都是描述的同一种数据结构。

    举个例子,上面这个图是典型的图结构(网络结构),人和公司,公司与公司都存在关联关系。这里面存在几个重要的概念,在网络结构中一家公司、一个人可以是一个;还有另外一个概念是,描述的是点与点之间的关系,对应上图中母公司和子公司之间的一个控股关系,也可以是某一个人是另外一个公司的董事长,这样的一个身份关系。点和边基本上组成一个网络结构。

    图结构在工业界使用的时候还会加上一个概念就是属性。比如,中间的这个人(点)可以有他的身份证、性别、年龄属性,关系就是边上也可以有一些属性,比如说投资某家公司的投资金额、投资的比例、投资的时间等等,都可以构成这个投资关系的属性。

    像基于上图这样的工商关系,微众银行、腾讯和我们(Nebula Graph)也都是有各自合作的项目。

    图数据库的案例 1:反欺诈

    图数据库洞察数据间的关联价值

    下面来介绍下图的应用。

    一般来说,图在安全场景里面的应用会比较多,上面这种图的中间部分是和 360金融合作的一个项目,主要用于识别诈骗团伙。

    具体来说,现在是互联网时代,很多 APP 支持申请贷款,不管是持牌或者持其他牌照的平台,都可以提供一定的贷款能力。与此同时,也存在团伙“作案”,当然抢银行的少,诈骗、骗贷的团伙多,而这些团伙是有特征的,可以通过一定的方式进行关联。

    在左边的这个例子里,有些的黑产团伙,他们控制的账号会登录一些设备(手机),这些设备和 Wi-Fi 有关联关系。通过这样的 账号-设备-WiFi 关联关系可以识别出来这些团伙。这些团伙被识别出来后,如果团伙相关的人来贷款或者说是来申请授信时,在风控环节会先识别出来。

    在中间这个例子里,红色的点是已知存在风险的账号,最中间的那个区域就是一个风险的团伙,这些节点就是被识别出来的风险节点,它们基于 Wi-Fi 关系将其他点关联到了一起。

    360 金融通过用图的方法大概识别了接近 100 万个有风险的团伙,所以这些团伙哪怕换一个马甲或者其他设备也能第一时间把他们识别出来,进行屏蔽。右边案例图是一些受害人,蓝色的点是诈骗团伙,诈骗团伙还是有挺明显的特征存在的。

    图数据库的案例 2:公司信用分

    图数据库洞察数据间的关联价值

    上面的例子主要是识别有风险的人,在这个例子里主要讲下 BOSS 直聘的公司风控。在上图中显示了 BOSS 直聘的一些公司,有些公司是很早入驻 BOSS 直聘平台,有些是新注册的。当中存在部分公司不一定可信,需要对这些公司作区分给一定信用分。比如说,公司信用等级好的对它的运营策略会放松点,信用等级差的公司对它的运营审核严格些。因为有不停的新的公司在注册,可以通过不同的运营方式得到这些公司的不同信息,上图这里用的是同这些公司有关联关系的关系公司。举个例子,我新注册一家公司的时候,这家公司还是会和其它公司有一些互动和关联,例如:该公司的分公司,或是公司同失信被执行人之间有关联关系,通过一轮轮的迭代算出风险评级和信用评级,为新出来的公司提供一个启动初始信用分,这个方法类似于网页权重中使用的 Page Rank。每天晚上 BOSS 直聘会跑几百万社区的权重。

    图数据库的案例 3:可信设备

    图数据库洞察数据间的关联价值

    这个例子比较直观。现在每个人基本上都有手机,然后会有常用的手机设备,也可能你会临时换一个设备。这个常用设备下,鉴权的要求比较低。而用临时设备鉴权的要求较高,风控等级较高。还有一种情况就是家人临时使用彼此的设备,这种情况下的鉴权要求可以不需要那么高。因为只要换一个新设备就判断为高风险场景的话,其实对于用户的侵入和打扰很明显。

    图数据库的案例 4:智能助理

    图数据库洞察数据间的关联价值

    这个是和美团合作的项目,本身其实是有两方面,一方面是和知识图谱相关,一方面是和深度学习相关。目前来说大多数公司的推荐系统都有基于深度学习的模型。那么会存在一个问题:这个推荐出来的内容可解释性差。简单来说,用户不知道为什么产生这样的列表。因此,美团结合图谱做了一些应用,把所有的菜、地点、人、人与人之间的关系还有这些东西组成大的网,当深度学习模型算出推荐列表之后,取用户和商家之间所有可能的关系,取出一条路径或者多条路径的,在多条路径之间做一些加权或者说计算一些路径规则分,呈现给用户看上去更可理解的理由。比如,这里挺有意思的理由,叫“在北京喜欢北京菜的山东老乡都说这家店很赞”,看到这个理由的时候,你会觉得这个推荐略微合理。当然类似的方法也可以用于像问答机器人这样的 KBQA 的系统。

    图数据库的案例 5:外部作弊与刷单

    图数据库洞察数据间的关联价值

    这是一个刷单的例子,其实很多公司会有运营经费,特别是对新用户会有运营经费,但是这会招来一些羊毛党。这些专业的羊毛党技术很好,他们来薅羊毛的速度比一般的消费者速度快很多,一般前期的大多数的运营经费不是给新用户用掉而是给羊毛党薅走了,羊毛党一般就是那些人,把他们识别出来之后,就可以降低运营经费被薅走的概率。

    图数据库的案例 6:数据治理系统

    图数据库洞察数据间的关联价值

    这个例子是关于 IT 系统的。我相信现在大多数的公司都是有一个庞大的数仓,几万张甚至几百万张的表,表与表之间又有比较强的依赖关系。例如:一张表或者某几张表取当中几个字段,通过一个 job 清洗,生成下一张表。某一天 DBA 或者某个业务人员发现有一个数据不太对,想知道哪个环节出错了,一层一层往上查,上百层的依赖,用图的方式关联就可以很快的查到哪个地方更有可能出错。这也是我们和微众银行合作的,他们现在正在使用的东西。

    图数据库的案例 7 / 8:服务依赖分析 / 代码依赖分析

    图数据库洞察数据间的关联价值

    左边是和运维相关的,右边是和研发相关的事。因为现在基本都是微服务化了,整个微服务之间的调用关系其实是很庞大的。特别是一个大型集团内的RPC 调用关系,运维自己都不一定搞得清楚全局是什么依赖情况。

    这个是美团的例子,把所有的调用关系写到图谱里,大概是百万级别的调用关系。比如说运维想知道过去 7 天可用率低于 4 个 9 的链路有哪些,可以非常快速地识别出来,深度可以是 10 层也可以 100 层。

    右边是一个辅助开发过程的小工具,对研发人员来说挺方便的。对于一个大型的代码仓库,函数之间相互调用。比如说研发今天想改用一个接口,但是我不知道有多少人在调用这个接口,是怎么调用的。对于测试来说,也不知道要测试哪些边界场景。那可以把这些关系都放到图谱里面去,这样大约是一个千万级别的调用关系,把整个调用关系全抽出来之后,那研发说我想看一眼这个接口被多少人调用了,调用方是怎么使用的;QA 要做测试的时候,可能有哪些边界场景受到影响也可以很快地知道。

    为什么使用图数据库

    图数据库洞察数据间的关联价值

    刚才说的其实就是一些图的应用,当然其实这些应用不用图这种数据结构来处理,也是可以的。比如在数仓用 Spark 或者写 SQL 来做也可以。但是为什么更推荐用图数据库呢?有以下几个原因。

    一图胜千言

    图数据库洞察数据间的关联价值

    一个是图的表达能力更强。左边是用表的结构方式来处理人物关系和社区关系。右边当中人的是比较重要的节点,他在左边的表中对应的某一行,右边是用图的方式来看。通过不同的着色可以很容易地看出来不同的社区,然后不同的社区之间通过某些特殊的节点来关联。这样远比用表的方式直观得多,特别是在右边图里面查找中心节点比起在左边的表中查找属性值大小要方便的多。

    繁琐 vs 简洁

    图数据库洞察数据间的关联价值

    第二个是对于图的遍历这种操作来说——相当于表操作中 join。如果用 SQL 来写,大约是左边这么长,也不是算非常复杂;但是用图的查询语言(右侧)来写的话,其实例子核心就是一句话,沿着一个点开始沿着这样一个路径取 Person 数据。所以对于图遍历操作,图专用的查询语言要更简洁。

    更快!

    图数据库洞察数据间的关联价值

    使用图还有一个优势是更快,行业内的经典例子就是查询的数据深度越多的时候,图数据库的优势越加明显。对于 4、 5 层深度的查询,小时级别的时延和秒级别的时延,是两种不同的业务形态。

    图数据库洞察数据间的关联价值

    最后一个原因是关于流行趋势。在国际上,用于统计各种数据库类型流行情况的 DB-Engines 上,可以看到图数据库的趋势。上图这是这个月最新的数据,绿色是图这种数据库类型流行的趋势,最下面红色的线是关系型数据库的流行趋势。可以看到,图数据库在过去 8 年内保持了比较好的增速,增长了 11 倍。

    为什么选择 Nebula Graph

    图数据库洞察数据间的关联价值

    当然,在整个图数据库领域,产品并不是只有 Nebula Graph 一个,也有很多的其他公司。今天早上也有其他同行在会场上,我想解释一下为什么会推荐 Nebula Graph。

    图数据库洞察数据间的关联价值

    一般大家选型基于不同的需求看的重点不一样,我想可能会对可靠性、成本性能、功能各个方面进行权衡。

    图数据库洞察数据间的关联价值

    Nebula Graph 是一个开源的产品,源代码是开放在 GitHub 上的。虽然产品的研发时间不长,从 2018 年 10 月开始第一行代码,但是整个项目很活跃。

    上图左下角是 Nebula Graph 中文论坛的情况,在国内有大量的使用者。而 Nebula Graph 本身是开源的项目,整个项目除了我们公司人员之外也有很多国内外贡献者,很多人在使用 Nebula 之后会发现一些 bug 这样就会 file 个 issue,也有不少人会自己动手 fix 和贡献 feature,这样提升了整个研发迭代速度。

    右边是所有 Nebula 的 GitHub 贡献者列表,这些是公开情况,你可以在 GitHub 上面看到。总的来说,贡献者来源很多,并不是背后只有一家公司在研发。

    图数据库洞察数据间的关联价值

    上图是在生产环境使用 Nebula Graph 的公司。

    图数据库洞察数据间的关联价值

    既然是 Nebula Graph 是开源代码的,那么所有人可以下载和评测。所有的用户都可以根据自己的业务场景做的评测,会更贴近自己的实际场景。而不像某些供应商自己提供的评测,用户难验证这样的评测里面隐藏了多少坑。

    这张图第一个例子是去年和微信合作的项目,他们现在的生产环境单个集群是 50 台机器的规模,它的更新数据量大概是 4,000 亿这样的级别。第二个是美团的例子,美团所做的 Nebula 和其它竞争对手产品的评测。因为美团也是有一个非常高的可用需求,基本上都是要两地多机房。第三个是 BOSS 直聘的评测,从友商产品迁移到 Nebula 之后,从最初只能做 50 亿量级的边的产品,提升到做千亿点边的项目。下面这是贝壳做的评测,公开在今年的DTCC,也是和友商产品的的对比。右边是 360 金融做的评测,生产环境服务器数量减少到原先集群的 1/3,性能是原来的 20 倍以上。

    图数据库洞察数据间的关联价值

    虽然软件本身是开源的,但是开源软件是可以商业化的。这个在国内外也是一个比较普遍的事情。Nebula 的源代码是开放的,所以不管是社区版也好,企业版也好,在产品功能内核、可视化、生态方面基本上没有太大的差别。主要的差别是在服务上,社区版如果有问题可以通过开源社区的方式来解决,按照开源协议(Apache 2.0)的约定。而如果是企业版的话,那会提供企业版的严格的 SLA。另外,云这几年流行和增速也非常的快,云目前是在受邀公测的阶段。大家有什么兴趣可以联系我们。

    谢谢大家!

    图数据库洞察数据间的关联价值

    查看原文

    赞 0 收藏 0 评论 0

    NebulaGraph 发布了文章 · 1月20日

    美团图数据库平台建设及业务实践

    本文为 #nLive vol.001|美团图数据库平台建设及业务实践# 主题演讲的文字稿,可前往 B站 观看本次视频

    美团图数据库平台建设及业务实践

    大家好,我是来自美团的赵登昌,今天我给大家分享下美团图数据库平台的建设以及业务实践。

    美团图数据库平台建设及业务实践

    这是本次报告的提纲,主要包括以下六方面内容。

    背景介绍

    美团图数据库平台建设及业务实践

    首先介绍下美团在图数据方面的业务需求。

    美团图数据库平台建设及业务实践

    美团内部有比较多的图数据存储以及多跳查询需求,总体来说有以下 4 个方面:

    第一个方面是知识图谱方向,美团内部有美食图谱、商品图谱、旅游图谱在内的近 10 个领域知识图谱,数据量级大概在千亿级别。在迭代、挖掘数据的过程中,需要一种组件对这些图谱数据进行统一管理。

    第二个方面是安全风控。业务部门有内容风控的需求,希望在商户、用户、评论中通过多跳查询来识别虚假评价;在支付时进行金融风控验证,实时多跳查询风险点。

    第三个方面是链路分析,比如说:数据血缘管理,公司数据平台上有很多 ETL Job,Job 和 Job 之间存在强弱依赖关系,这些强弱依赖关系形成了一张图,在进行 ETL Job 的优化或者故障处理时,需要对这个图进行分析。类似的需求还有代码分析、服务治理等。

    第四个方面是组织架构管理,包括:公司组织架构的管理,实线汇报链、虚线汇报链、虚拟组织的管理,以及商家连锁门店的管理。比如,需要管理一个商家在不同区域都有哪些门店,能够进行多层关系查找或者逆向关系搜索。

    总体来说,我们需要一种组件来管理千亿级别的图数据,解决图数据存储以及多跳查询问题。

    美团图数据库平台建设及业务实践

    传统的关系型数据库、NoSQL 数据库可以用来存储图数据,但是不能很好处理图上多跳查询这一高频操作。Neo4j 公司在社交场景做了 MySQL 和 Neo4j 的多跳查询性能对比,具体测试场景是,在一个 100 万人、每个人大概有 50 个朋友的社交网络里找最大深度为 5 的朋友的朋友。从测试结果看,查询深度增大到 5 时, MySQL 已经查不出结果了,但 Neo4j 依然能在秒级返回数据。实验结果表明多跳查询中图数据库优势明显。

    图数据库选型

    美团图数据库平台建设及业务实践

    下面介绍我们的图数据库选型工作。

    美团图数据库平台建设及业务实践

    我们主要有以下 5 个图数据库选型要求

    • A. 项目开源,暂时不考虑需要付费的图数据库;
    • B. 分布式的架构设计,具备良好的可扩展性;
    • C. 毫秒级的多跳查询延迟;
    • D. 支持千亿量级点边存储;
    • E. 具备批量从数仓导入数据的能力。

    我们分析了 DB-Engine 上排名 Top30 的图数据库,剔除不开源的项目后,把剩下的图数据库分成三类。

    • 第一类包括 Neo4j、ArangoDB、Virtuoso、TigerGraph、RedisGraph。此类图数据库只有单机版本开源可用,性能比较优秀,但是不能应对分布式场景中数据的规模增长,即不满足选型要求 B、D;
    • 第二类包括 JanusGraph、HugeGraph。此类图数据库在现有存储系统之上新增了通用的图语义解释层,图语义层提供了图遍历的能力,但是受到存储层或者架构限制,不支持完整的计算下推,多跳遍历的性能较差,很难满足 OLTP 场景下对低延时的要求,即不满足选型要求 C;
    • 第三类包括 Dgraph、Nebula Graph。此类图数据库根据图数据的特点对数据存储模型、点边分布、执行引擎进行了全新设计,对图的多跳遍历进行了深度优化,基本满足我们对图数据库的选型要求

    美团图数据库平台建设及业务实践

    之后我们在一个公开社交数据集上(大约 200 亿点边)对 Nebula Graph 、Dgraph 、HugeGraph 进行了深度性能测评。主要从三个方面进行评测:

    • 批量数据的导入
    • 实时写入性能测试
    • 数据多跳查询性能测试

    测试详情见 Nebula 论坛:主流开源分布式图数据库Benchmark 🔗

    美团图数据库平台建设及业务实践

    从测试结果看 Nebula Graph 在数据导入、实时写入及多跳查询方面性能均优于竞品。此外,Nebula Graph 社区活跃,问题的响应速度快,所以团队最终选择了基于 Nebula Graph 来搭建图数据库平台。

    图数据库平台建设

    美团图数据库平台建设及业务实践

    下面介绍美团图数据库平台的建设,整个图数据库平台包括 4 层。

    美团图数据库平台建设及业务实践

    第一层是数据生产层,平台的图数据主要有两种来源,第一种是业务方把数仓中数据通过 ETL Job 转成点和边的 Hive 表,然后离线导入到图数据库中;第二种是业务线上实时产生的数据、或者通过 Spark、Flink 等流式处理产生的近线数据,实时地通过 Nebula Graph 提供的在线批量接口灌到图数据库中。

    第二层是数据存储层,平台支持两种图数据库集群的部署方式。

    • 第一种是 CP 方案,即 Consistency & Partition tolerance,这是 Nebula Graph 原生支持的集群部署方式。单集群部署,集群中机器数量大于等于副本的数量,副本数量大于等于 3 。只要集群中有大于副本数一半的机器存活,整个集群就可以对外正常提供服务。CP 方案保证了数据读写的强一致性,但这种部署方式下集群可用性不高。
    • 第二种部署方式是 AP 方案,即 Availability & Partition tolerance,在一个应用中部署多个图数据库集群,每个集群数据副本数为 1 ,多集群之间进行互备。这种部署方式的好处在于整个应用对外的可用性高,但数据读写的一致性要差点。

    第三层是数据应用层,业务方可以在业务服务中引入平台提供的图谱 SDK,实时地对图数据进行增删改查。

    第四层是支撑平台,提供了 Schema 管理、权限管理、数据质检、数据增删改查、集群扩缩容、图谱画像、图数据导出、监控报警、图可视化、集群包管理等功能。

    经过这四层架构设计,目前图数据库平台基本具备了对图数据的一站式自助管理功能。如果某个业务方要使用这种图数据库能力,那么业务方可以在这个平台上自助地创建图数据库集群、创建图的 Schema、导入图数据、配置导入数据的执行计划、引入平台提供的 SDK 对数据进行操作;平台侧主要负责各业务方图数据库集群的稳定性。目前有三、四十个业务在平台上真正落地,基本能满足各个业务方需求。

    美团图数据库平台建设及业务实践

    再介绍下图数据库平台中几个核心模块的设计。

    高可用模块设计

    首先是单应用多集群高可用模块的设计(AP 方案)。为什么有 AP 方案的设计呢?因为接入这个图数据库平台的业务方比较在意的指标是集群可用性。在线服务对集群的可用性要求非常高,最基础的要求是集群可用性能达到 4 个 9,即一年里集群的不可用时间要小于一个小时,对于在线服务来说,服务或者集群的可用性是整个业务的生命线,如果这点保证不了,即使集群提供的能力再多再丰富,那么业务方也不会考虑使用,可用性是业务选型的基础。

    另外公司要求中间件要有跨区域容灾能力,即要具备在多个地域部署多集群的能力。我们分析了平台接入方的业务需求,大约 80% 的场景是 T+1 全量导入数据、线上只读;在这种场景下对图数据的读写强一致性要求并不高,因此我们设计了单应用多集群这种部署方案。

    美团图数据库平台建设及业务实践

    部署方式可以参考上图,一个业务方在图数据库平台上创建了 1 个应用并部署了 4 个集群,其中北京 2 个、上海 2 个,平时这 4 个集群同时对外提供服务。假如现在北京集群 1 挂了,那么北京集群 2 可以提供服务。如果说真那么不巧,北京集群都挂了,或者对外的网络不可用,那么上海的集群可以提供服务,这种部署方式下,平台会尽可能地通过一些方式来保证整个应用的可用性。然后每个集群内部尽量部署同机房的机器,因为图数据集群内部 RPC 是非常多的,如果有跨机房或者跨区域的频繁调用,整个集群对外的性能会比较低。

    美团图数据库平台建设及业务实践

    这个 AP 模块的设计主要包含下面 4 个部分:

    • 第一部分是右侧的图数据库 Agent,它是部署在图数据库集群的一个进程,用来收集机器和Nebula Graph 三个核心模块的信息,并上报到图数据库平台。Agent 能够接收图数据库平台的命令并对图数据库进行操作。
    • 第二部分是图管理平台,它主要是对集群进行管理,并同步图数据库集群的状态到配置中心。
    • 第三部分是图数据库 SDK,它主要做的事情是管理连接到图数据库集群的连接。如果业务方发送了某个查询请求,SDK 会进行集群的路由和负载均衡,选择出一条高质量的连接来发送请求。此外,SDK 还会处理图数据库集群中问题机器的自动降级以及恢复,并且要支持平滑切换集群的数据版本。
    • 第四部分是配置中心,类似 ZooKeeper,存储集群的当前状态。

    每小时百亿级数据导入模块设计

    美团图数据库平台建设及业务实践

    第二个模块是每小时百亿量级数据导入模块,上面说了业务场景里 80% 是 T+1 全量导入数据,然后线上只读。平台在 19 年底 / 20 年初全量导入数据的方式是调用 Nebula Graph 对外提供的批量数据导入接口,这种方式的数据写入速率大概是每小时 10 亿级别,导入百亿数据大概要耗费 10 个小时,这个时间有点久。此外,在以几十万每秒的速度导数据的过程中,会长期占用机器的 CPU、IO 资源,一方面会对机器造成损耗,另一方面数据导入过程中集群对外提供的读性能会变弱。

    为了解决上面两个问题,平台进行了如下优化:在 Spark 集群中直接生成图数据库底层文件 sst file,再借助 RocksDB 的 bulkload 功能直接 ingest 文件到图数据库。这部分提速优化工作在 19 年底的时候就开始了,但是中间遇到 core dump 问题没有上线。在 20 年六、七月份的时候,微信的本利大佬向社区提交了这方面的 pr,和他在线沟通之后解决了我们的问题,在这里感谢一下本利大佬 💐 。

    美团图数据库平台建设及业务实践

    图数据库平台数据导入的核心流程可以看右边这张图,当用户在平台上提交了导数据操作后,图数据库平台会向公司的 Spark 集群提交一个 Spark 任务,在 Spark 任务中会生成图数据库里相关的点、边以及点索引、边索引相关的 sst 文件,并上传到公司的 S3 云存储上。文件生成后,图数据库平台会通知应用里的多个集群去下载这些存储文件,之后完成 ingest 跟 compact 操作,最后完成数据版本的切换。

    平台方为兼顾各个业务方的不同需求,统一了应用导入、集群导入、离线导入、在线导入以及全量导入、增量导入这些场景,然后细分成下面九个阶段,从流程上保证在导数据过程中应用整体的可用性。

    • sst file生成
    • sst file下载
    • ingest
    • compact
    • 数据校验
    • 增量回溯
    • 数据版本切换
    • 集群重启
    • 数据预热

    实时写入多集群数据同步模块设计

    美团图数据库平台建设及业务实践

    第三个模块是实时写入多集群数据同步,平台有 15% 的需求场景是在实时读取数据时,还要把新产生的业务数据实时写入集群,并且对数据的读写强一致性要求不高,就是说业务方写到图数据库里的数据,不需要立马能读到。

    针对上述场景,业务方在使用单应用多集群这种部署方案时,多集群里的数据需要保证最终一致性。平台做了以下设计,第一部分是引入 Kafka 组件,业务方在服务中通过 SDK 对图数据库进行写操作时,SDK 并不直接写图数据库,而是把写操作写到 Kafka 队列里,之后由该应用下的多个集群异步消费这个 Kafka 队列

    美团图数据库平台建设及业务实践

    第二部分是集群在应用级别可配置消费并发度,来控制数据写入集群的速度。具体流程是

    • SDK 对用户写操作语句做语法解析,将其中点边的批量操作拆解成对单个点边操作,即对写语句做一次改写
    • Agent 消费 Kafka 时确保每个点及其出边相关操作在单个线程里顺序执行,保证这点就能保证各个集群执行完写操作后最终的结果是一致的。
    • 并发扩展:通过改变 Kafka 分片数、Agent 中消费 Kafka 线程数来变更和调整 Kafka 中操作的消费速度。
    • 如果未来 Nebula Graph 支持事务的话,上面的配置需要调整成单分片单线程消费,平台需要对设计方案再做优化调整。

    美团图数据库平台建设及业务实践

    第三部分是在实时写入数据过程中,图数据库平台可以同步生成一个全量数据版本,并做平滑切换,通过右图里的流程来确保数据的不重不漏不延迟

    图可视化模块设计

    美团图数据库平台建设及业务实践

    第四个模块是图可视化模块,平台在 2020 年上半年调研了 Nebula Graph 官方的图可视化设计跟一些第三方开源的可视化组件,然后在图数据库平台上增加了通用的图可视化功能,主要是用于解决子图探索问题;当用户在图数据库平台通过可视化组件查看图数据时,能尽量通过恰当的交互设计来避免因为节点过多而引发爆屏。

    目前,平台上的可视化模块有下面几个功能。

    第一个是通过 ID 或者索引查找顶点。

    第二个是能查看顶点和边的卡片(卡片中展示点边属性和属性值),可以单选、多选、框选以及按类型选择顶点。

    美团图数据库平台建设及业务实践

    第三个是图探索,当用户点击某个顶点时,系统会展示它的一跳邻居信息,包括:该顶点有哪些出边?通过这个边它能 Touch 到几个点?该顶点的入边又是什么情况?通过这种一跳信息的展示,用户在平台上探索子图的时候,可快速了解到周边的邻居信息,更快地进行子图探索。在探索过程中,平台也支持对边进行过滤。

    第四个是图编辑能力,让平台用户在不熟悉 Nebula Graph 语法的情况下也能增删改点边数据,对线上数据进行临时的干预。

    业务实践

    美团图数据库平台建设及业务实践

    美团图数据库平台建设及业务实践

    下面来介绍下接入我们平台的一些落地项目。

    第一个项目是智能助理,它的数据是基于美团商户数据、用户评论构建的餐饮娱乐知识图谱,覆盖美食、酒店、旅游等领域,包含 13 类实体和 22 类关系。目前点边数量大概在百亿级别,数据是 T+1 全量更新,主要用于解决搜索或者智能助理里 KBQA(全称:Knowledge Based Question Answer)类的问题。核心处理流程是通过 NLP 算法识别关系和实体后构造出 Nebula Graph SQL 语句,再到图数据库获取数据。

    美团图数据库平台建设及业务实践

    典型的应用场景有商场找店,比如,某个用户想知道望京新荟城这个商场有没有海底捞,智能助理就能快速查出结果告诉用户。

    美团图数据库平台建设及业务实践

    还有一个典型场景是标签找店,想知道望京 SOHO 附近有没有适合情侣约会的餐厅,或者你可以多加几个场景标签,系统都能给你查找出来。

    美团图数据库平台建设及业务实践

    第二个是搜索召回,数据主要是基于医美商家信息构建的医美知识图谱,包含 9 类实体和 13 类关系,点边数量在百万级别,同样也是 T+1 全量更新,主要用于大搜底层实时召回,返回与 query 相关的商户、产品或医生信息,解决医美类搜索词少结果、无结果问题。比如,某个用户搜“啤酒肚”这种症状、或者“润百颜”这类品牌,系统都能给他召回相关的医美门店。

    美团图数据库平台建设及业务实践

    第三个是图谱推荐理由,数据来自用户的画像信息、商户的特征信息、用户半年内收藏/购买行为,现在的数据量级是 10 亿级别, T+1 全量更新。这个项目的目标是给出美食列表推荐商户的可解释理由。为什么会做这个事呢?现在美团 App 和点评 App 上默认的商户推荐列表是由深度学习模型生成的,但模型并不会给出生成这个列表的理由,缺少可解释性。然而在图谱里用户跟商户之间天然存在多条连通路径,我们希望能选出一条合适路径来生成推荐理由,在 App 界面上展示给用户推荐某家店的原因。比如我们可以基于用户的协同过滤算法来生成推荐理由,在家乡、消费水平、偏好类目、偏好菜系等多个组合维度中找出多条路径,然后给这些路径打分,选出一条分值较高的路径,之后按照特定 pattern 产出推荐理由。通过上述方式,就可以获得在北京喜欢北京菜的山东老乡都说这家店很赞,或者广州老乡都中意他家的正宗北京炸酱面这类理由。

    美团图数据库平台建设及业务实践

    第四个是代码依赖分析,是把公司里的代码库中代码依赖关系写到图数据库。公司代码库里有很多服务代码,这些服务都会有对外提供的接口,这些接口的实现依赖于该服务中某些类的成员函数,这些类的成员函数又依赖了本类的成员变量、成员函数、或者其它类的成员函数,那么它们之间的依赖关系就形成了一张图,我们把这个图写到图数据库里,做什么事呢?

    典型场景是 QA 的精准测试,当 RD 完成需求并向公司的代码仓库提交了他的 pr 后,这些更改会实时地写到图数据库中,所以 RD 就能查到他所写的代码影响了哪些外部接口,并且能展示出调用路径来。如果 RD 本来是要改接口 A 的行为,他改了很多东西,但是他可能并不知道他改的东西也会影响到对外接口 B、C、D,这时候就可以用代码依赖分析来做个 Check。

    美团图数据库平台建设及业务实践

    第五个是服务治理,美团内部有几十万个微服务,这些微服务之间存在互相调用关系,这些调用关系形成了一张图。我们把这些调用关系实时写入图数据库里,然后做一些服务链路治理和告警优化工作。

    美团图数据库平台建设及业务实践

    第六个项目是数据血缘,把数仓中 ETL 任务的依赖关系写到了图数据库中,大概是千万级别的数据量级,数据实时写入,每天凌晨做一次全量 reload,主要是用来查找数据任务的上下游依赖。比如说,通过这个 FIND NOLOOP PATH FROM hash('task1') OVER depend WHERE depend.type == '强依赖' UPTO 50 STEPS 语句找出 task1 这个任务的所有强依赖路径。这里,我们针对 Nebula Graph 官方的 FIND PATH 功能做了一些加强,添加了无环路径的检索跟 WHERE 语句过滤。

    美团和 Nebula

    美团图数据库平台建设及业务实践

    最后,来介绍下团队对社区的贡献。

    美团图数据库平台建设及业务实践

    为了更好地满足图数据库平台上用户的需求,我们对 Nebula Graph 1.0的内核做了部分功能的扩充和部分性能的优化,并把相对来说比较通用的功能给 Nebula Graph 社区提了 PR,也向社区公众号投稿了一篇 主流开源分布式图数据库Benchmark 🔗

    当然,我们通过 Nebula Graph 解决了公司内的很多业务问题,目前对 Nebula Graph 社区做的贡献还比较少,后续会加强在社区技术共享方面的工作,希望能够培养出越来越多的 Nebula Committer。

    美团图数据库平台的未来规划

    美团图数据库平台建设及业务实践

    美团图数据库平台建设及业务实践

    未来规划主要有两个方面,第一方面是等 Nebula Graph 2.0 的内核相对稳定后,在我们图数据库平台上适配 Nebula Graph 2.0 内核。第二方面是去挖掘更多的图数据价值。现在美团图数据库平台支持了图数据存储及多跳查询这种基本能力,后续我们打算基于 Nebula Graph 去探索一下图学习、图计算的能力,给平台用户提供更多挖掘图数据价值的功能。

    以上为本次美团 NLP 技术专家——赵登昌老师带来的图数据库平台建设方面的分享。

    如果你对【图存储】、【图学习】、【图计算】感兴趣,欢迎向赵登昌老师投递简历,投递邮箱:zhaodengchang@meituan.com。

    喜欢这篇文章?来来来,给我们的 GitHub 点个 star 表鼓励啦~~ 🙇‍♂️🙇‍♀️ [手动跪谢]

    交流图数据库技术?交个朋友,Nebula Graph 官方小助手微信:NebulaGraphbot 拉你进交流群~~

    查看原文

    赞 1 收藏 0 评论 0

    NebulaGraph 发布了文章 · 1月11日

    Nebula Exchange 工具 Hive 数据导入的踩坑之旅

    Nebula Exchange 工具 Hive 数据导入的踩坑之旅

    摘要:本文由社区用户 xrfinbj 贡献,主要介绍 Exchange 工具从 Hive 数仓导入数据到 Nebula Graph 的流程及相关的注意事项。

    1 背景

    公司内部有使用图数据库的场景,内部通过技术选型确定了 Nebula Graph 图数据库,还需要验证 Nebula Graph 数据库在实际业务场景下的查询性能。所以急迫的需要导入数据到 Nebula Graph 并验证。在这个过程中发现通过 Exchange 工具从 hive 数仓导入数据到 Nebula Graph 文档不是很全,所以把这个流程中踩到的坑记录下来,回馈社区,避免后人走弯路。

    本文主要基于我之前发在论坛的 2 篇帖子:

    2 环境信息

    • Nebula Graph 版本:nebula:nightly
    • 部署方式(分布式 / 单机 / Docker / DBaaS):Mac 电脑 Docker 部署
    • 硬件信息

      • 磁盘(SSD / HDD):Mac 电脑 SSD
      • CPU、内存信息:16 G
    • 数仓环境(Mac 电脑搭建的本地数仓):

      • Hive 3.1.2
      • Hadoop 3.2.1
    • Exchange 工具:https://github.com/vesoft-inc/nebula-java/tree/v1.0/tools/exchange

    编译后生成 jar 包

    • Spark

    spark-2.4.7-bin-hadoop2.7 (conf 目录下配置 Hadoop 3.2.1 对应的 core-site.xml,hdfs-site.xml,hive-site.xml 设置 spark-env.sh)
    Scala code runner version 2.13.3 -- Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.

    3 配置

    1 Nebula Graph DDL

    CREATE SPACE test_hive(partition_num=10, replica_factor=1); --创建图空间,本示例中假设只需要一个副本
    USE test_hive; --选择图空间 test
    CREATE TAG tagA(idInt int, idString string, tboolean bool, tdouble double); -- 创建标签 tagA
    CREATE TAG tagB(idInt int, idString string, tboolean bool, tdouble double); -- 创建标签 tagB
    CREATE EDGE edgeAB(idInt int, idString string, tboolean bool, tdouble double); -- 创建边类型 edgeAB

    2 Hive DDL

    CREATE TABLE `tagA`(                               
       `id` bigint,                                     
       `idInt` int,                            
       `idString` string,                                 
       `tboolean` boolean,                                 
       `tdouble` double) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\001' LINES TERMINATED BY '\n';
    insert into tagA select 1,1,'str1',true,11.11;
    insert into tagA select 2,2,"str2",false,22.22;
    
    CREATE TABLE `tagB`(                               
       `id` bigint,                                     
       `idInt` int,                            
       `idString` string,                                 
       `tboolean` boolean,                                 
       `tdouble` double) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\001' LINES TERMINATED BY '\n';
    insert into tagB select 3,3,"str 3",true,33.33;
    insert into tagB select 4,4,"str 4",false,44.44;
    
    CREATE TABLE `edgeAB`(                               
       `id_source` bigint,                                     
       `id_dst` bigint,         
       `idInt` int,                            
       `idString` string,                                 
       `tboolean` boolean,                                 
       `tdouble` double) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\001' LINES TERMINATED BY '\n';
    insert into edgeAB select 1,3,5,"edge 1",true,55.55;
    insert into edgeAB select 2,4,6,"edge 2",false,66.66;

    3 我的最新 nebula_application.conf 文件

    注意看exec、fields、nebula.fields、vertex、source、target字段映射

    {
      # Spark relation config
      spark: {
        app: {
          name: Spark Writer
        }
    
        driver: {
          cores: 1
          maxResultSize: 1G
        }
    
        cores {
          max: 4
        }
      }
    
      # Nebula Graph relation config
      nebula: {
        address:{
          graph: ["192.168.1.110:3699"]
          meta: ["192.168.1.110:45500"]
        }
        user: user
        pswd: password
        space: test_hive
    
        connection {
          timeout: 3000
          retry: 3
        }
    
        execution {
          retry: 3
        }
    
        error: {
          max: 32
          output: /tmp/error
        }
        rate: {
          limit: 1024
          timeout: 1000
        }
      }
    
      # Processing tags
      tags: [
        # Loading from Hive
        {
          name: tagA
          type: {
            source: hive
            sink: client
          }
          exec: "select id,idint,idstring,tboolean,tdouble from nebula.taga"
          fields: [id,idstring,tboolean,tdouble]
          nebula.fields: [idInt,idString,tboolean,tdouble]
          vertex: id
          batch: 256
          partition: 10
        }
        {
          name: tagB
          type: {
            source: hive
            sink: client
          }
          exec: "select id,idint,idstring,tboolean,tdouble from nebula.tagb"
          fields: [id,idstring,tboolean,tdouble]
          nebula.fields: [idInt,idString,tboolean,tdouble]
          vertex: id
          batch: 256
          partition: 10
        }
      ]
    
      # Processing edges
      edges: [
        # Loading from Hive
        {
          name: edgeAB
          type: {
            source: hive
            sink: client
          }
          exec: "select id_source,id_dst,idint,idstring,tboolean,tdouble from nebula.edgeab"
          fields: [id_source,idstring,tboolean,tdouble]
          nebula.fields: [idInt,idString,tboolean,tdouble]
          source: id_source
          target: id_dst
          batch: 256
          partition: 10
        }
      ]
    }
    

    4 执行导入

    4.1 确保 nebula 服务启动

    4.2 确保 Hive 表和数据就绪

    4.3 执行 spark-sql cli 查看 Hive 表以及数据是否正常以确保 Spark 环境没问题

    Nebula Exchange 工具 Hive 数据导入的踩坑之旅

    4.4 一切配置工作就绪后,执行 Spark 命令:

    spark-submit --class com.vesoft.nebula.tools.importer.Exchange --master “local[4]” /xxx/exchange-1.0.1.jar -c /xxx/nebula_application.conf -h

    4.5 导入成功后 可以借助 db_dump 工具查看导入数据量 验证正确性

    ./db_dump --mode=stat --space=xxx --db_path=/home/xxx/data/storage0/nebula   --limit 20000000

    5 踩坑以及说明

    • 第一个坑就是 spark-submit 命令没有加 -h 参数
    • Nebula Graph 中 tagName 是大小写敏感的,tags 的配置中 name 配置的应该是 Nebula Graph 的 tag 名
    • Hive的 int 和 Nebula Graph 的 int 不一致,Hive 里面的 bigint 对应 Nebula Graph 的 int

    其他说明:

    • 由于 Nebula Graph 底层存储是 kv,重复插入其实是覆盖,update 操作用 insert 替代性能会高些
    • 文档里面不全的地方可能暂时只有一边看源码解决,一边去论坛问(开发同学也不容易又要紧张的开发又要回答用户的疑问)
    • 导入数据、Compact 以及操作建议:https://docs.nebula-graph.com.cn/manual-CN/3.build-develop-and-administration/5.storage-service-administration/compact/
    • 我已经验证如下两个场景:

      • 用 Spark 2.4 从 Hive 2(Hadoop 2)中导入数据到 Nebula Graph
      • 用 Spark 2.4 从 Hive3(Hadoop 3)中导入数据到 Nebula Graph

    说明:Exchange 目前还不支持 Spark 3,编译后运行报错,所以没法验证 Spark 3 环境

    还有一些疑问

    • nebula_application.conf 文件的参数 batch 和 rate.limit 应该如何设置?参数如何抉择?
    • Exchange 工具 Hive 数据导入原理(Spark 这块我也是最近现学现用)

    6 Exchange 源码 Debug

    Spark Debug 部分参考博客:https://dzone.com/articles/how-to-attach-a-debugger-to-apache-spark

    通过 Exchange 源码的学习和 Debug 能加深对 Exchange 原理的理解,同时也能发现一些文档描述不清晰的地方,比如 导入 SST 文件Download and Ingest 只有结合源码看才能发现文档描述不清晰逻辑不严谨的问题。

    通过源码 Debug 也能发现一些简单的参数配置问题。

    进入正题:

    步骤一:

    export SPARK_SUBMIT_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=4000

    步骤二:

    spark-submit --class com.vesoft.nebula.tools.importer.Exchange --master “local” /xxx/exchange-1.1.0.jar -c /xxx/nebula_application.conf -h
    Listening for transport dt_socket at address: 4000

    步骤三:IDEA 配置

    IDEA 配置

    步骤四:在 IDEA 里面点击 Debug

    IDEA Debug

    7 建议与感谢

    感谢 vesoft 提供了宇宙性能最强的 Nebula Graph 图数据库,能解决业务中很多实际问题,中途这点痛不算什么(看之前的分享,360 数科他们那个痛才是真痛)。中途遇到的问题都有幸得到社区及时的反馈解答,再次感谢

    很期待 Exchange 支持 Nebula Graph 2.0

    参考资料

    喜欢这篇文章?来来来,给我们的 GitHub 点个 star 表鼓励啦~~ 🙇‍♂️🙇‍♀️ [手动跪谢]

    交流图数据库技术?交个朋友,Nebula Graph 官方小助手微信:NebulaGraphbot 拉你进交流群~~

    推荐阅读

    查看原文

    赞 0 收藏 0 评论 0

    认证与成就

    • 获得 40 次点赞
    • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

    擅长技能
    编辑

    (゚∀゚ )
    暂时没有

    开源项目 & 著作
    编辑

    注册于 2019-07-23
    个人主页被 3.1k 人浏览