Nebula Graph介绍和SpringBoot环境连接和查询

转载请注明来源 https://www.cnblogs.com/milton/p/16784098.html

说明

当前Nebula Graph的最新版本是3.2.1, 根据官方的文档进行配置

https://docs.nebula-graph.io/3.2.1/14.client/4.nebula-java-client/

Nebula Graph 的一些特点

  1. 支持分布式. 相对于Neo4j, TigerGraph这些图数据库, Nebula 是面向分布式设计的, 因此对集群的支持比较完备, 在规模上上限要高很多. 在实际项目中存储了180亿的点边, 这个数量对于Neo4j和TigerGraph是比较困难的.
  2. 支持图空间. 各个图空间的ID是互不干扰的, 但是在同一个图空间里ID的类型和长度必须一致. 注意这个一致约束的是所有的点和边. Nebula 可以使用int64作为ID, 也可以用字符串, 但是字符串需要指定一个长度, 例如64个字节. 相对于只能用长整数的Neo4j, ID设计上更自由灵活.
  3. 点对应的类型叫TAG, 边对应的类型叫EDGE
    1. TAG和EDGE都会对应一组的属性(map, 或者说dict)
    2. 一个点可以对多个TAG, 每个TAG一组属性, 多组属性. 项目中建议一开始不要用多TAG, 在整个图结构稳定后, 再做合并
    3. 一个边只对应一个EDGE, 一组属性
  4. Nebula 用的是自定义的查询语法 GQL, 和 cypher 语法基本一样
  5. 除了点边的ID和关联关系外, 只有带索引的属性可以查询. 这点和其它图数据库不一样, 其它数据库即使没有索引, 慢是慢点但是不报错, Nebula直接给你返回错误.
  6. 对于返回数量较大的查询, Nebula会强制查询必须带limit
  7. Nebula 单节点稳定性是有问题的, 在3.2.1版本中观察到偶尔会出现服务自行退出, 如果在生产环境使用, 需要有后台监控进行心跳检测和自动启动

GQL 常用查询

下面列出一些常用的查询

-- 列出图空间
SHOW SPACES; -- 列出tag(点类型)和edge(边类型), 需要先 USE 一个图空间
SHOW TAGS;
SHOW EDGES;

列出某一类型的点和边

MATCH ()-[e:follow]-() RETURN e
MATCH (v:player) RETURN v

带条件的查询, 在结果数量较多时必须带limit, 否则Nebula会报错

match (v:ADDRESS)-[e]-() where id(v)==\"ADD:82388116\" return v,e limit 100

基础配置和使用

在上面的链接中, 提供了最小的配置和测试代码

pom.xml 增加包依赖

对于Nebula Graph 3.2.1, 需要使用3.0.0的版本. client的每个版本只能对应特定的一两个服务端版本

<dependency>
<groupId>com.vesoft</groupId>
<artifactId>client</artifactId>
<version>3.0.0</version>
</dependency>

Java调用

Java调用主要是三部分, 创建连接池, 创建会话, 执行查询

创建 NebulaPool 连接池

连接到地址127.0.0.1, 端口9669, 连接池大小100. 注意地址和端口是一个列表, Nebula是支持集群的. 连接时不需要用户和密码

NebulaPool pool = new NebulaPool();
try {
NebulaPoolConfig nebulaPoolConfig = new NebulaPoolConfig();
nebulaPoolConfig.setMaxConnSize(100);
List<HostAddress> addresses = Arrays.asList(new HostAddress("127.0.0.1", 9669));
Boolean initResult = pool.init(addresses, nebulaPoolConfig);
if (!initResult) {
log.error("pool init failed.");
return;
}
} catch ()
//...

创建 Session 会话

创建会话时需要用户名和密码

Session session = pool.getSession("root", "nebula", false);

执行查询

创建一个SPACE, 然后使用这个SPACE, 创建一个TAG person, 创建一个EDGE like

String createSchema = "CREATE SPACE IF NOT EXISTS test(vid_type=fixed_string(20)); "
+ "USE test;"
+ "CREATE TAG IF NOT EXISTS person(name string, age int);"
+ "CREATE EDGE IF NOT EXISTS like(likeness double)";
ResultSet resp = session.execute(createSchema);
if (!resp.isSucceeded()) {
log.error(String.format("Execute: `%s', failed: %s",
createSchema, resp.getErrorMessage()));
System.exit(1);
}

添加一个点记录

String insertVertexes = "INSERT VERTEX person(name, age) VALUES "
+ "'Bob':('Bob', 10), "
+ "'Lily':('Lily', 9), "
+ "'Tom':('Tom', 10), "
+ "'Jerry':('Jerry', 13), "
+ "'John':('John', 11);";
ResultSet resp = session.execute(insertVertexes);
if (!resp.isSucceeded()) {
log.error(String.format("Execute: `%s', failed: %s",
insertVertexes, resp.getErrorMessage()));
System.exit(1);
}

查询

String query = "GO FROM \"Bob\" OVER like "
+ "YIELD $^.person.name, $^.person.age, like.likeness";
ResultSet resp = session.execute(query);
if (!resp.isSucceeded()) {
log.error(String.format("Execute: `%s', failed: %s",
query, resp.getErrorMessage()));
System.exit(1);
}
printResult(resp);

在 SpringBoot 项目中使用 Nebula Graph

pom.xml 增加包依赖

<dependency>
<groupId>com.vesoft</groupId>
<artifactId>client</artifactId>
<version>3.0.0</version>
</dependency>

Session工厂: NebulaSessionFactory.java

配合@Bean(destroyMethod = "close"), 创建一个工厂类, 接收pool并实现close()方法

public class NebulaSessionFactory {
private final NebulaPool pool;
private final String username;
private final String password; public NebulaSessionFactory(NebulaPool pool, String username, String password) {
this.pool = pool;
this.username = username;
this.password = password;
} public Session getSession() {
try {
return pool.getSession(username, password, false);
} catch (NotValidConnectionException|IOErrorException|AuthFailedException|ClientServerIncompatibleException e) {
throw new RuntimeException("Nebula session exception", e);
}
} public void close() {
pool.close();
}
}

为什么不直接将 NebulaPool 配置为Bean? 因为 Session 每次创建时需要带用户名密码, 将密码作为config注入到每个Service中肯定是大家都不愿意看到的.

配置修改: application.yml

  • 这里的值如果不打算使用profile配置, 可以直接写入
  • hosts是逗号分隔的地址端口列表, 例如 10.22.33.33:9669,10.22.33.34:9669
myapp:
nebula:
hosts: @nebula.hosts@
username: @nebula.username@
password: @nebula.password@
max-conn: @nebula.max-conn@

Spring启动配置: NebulaGraphConfig.java

应用启动时读取配置, 创建 NebulaPool, 并实例化 NebulaSessionFactory, destroyMethod = "close", 这个表示在项目shutdown时会调用Bean的close方法释放资源.

@Configuration
public class NebulaGraphConfig { @Value("${myapp.nebula.hosts}")
private String hosts;
@Value("${myapp.nebula.max-conn}")
private int maxConn;
@Value("${myapp.nebula.username}")
private String username;
@Value("${myapp.nebula.password}")
private String password; @Bean(destroyMethod = "close")
public NebulaSessionFactory nebulaSessionFactory() {
List<HostAddress> hostAddresses = new ArrayList<>();
String[] hostList = hosts.split(",[ ]*");
for (String host : hostList) {
String[] hostParts = host.split(":");
if (hostParts.length != 2 || !hostParts[1].matches("\\d+")) {
throw new RuntimeException("Invalid host name set for Nebula: " + host);
}
hostAddresses.add(new HostAddress(hostParts[0], Integer.parseInt(hostParts[1])));
}
NebulaPoolConfig poolConfig = new NebulaPoolConfig();
poolConfig.setMaxConnSize(maxConn);
NebulaPool pool = new NebulaPool();
try {
pool.init(hostAddresses, poolConfig);
} catch (UnknownHostException e) {
throw new RuntimeException("Unknown Nebula hosts");
}
return new NebulaSessionFactory(pool, username, password);
}
}

Service调用

在 Service 中进行调用

@Service
@Slf4j
public class GraphServiceImpl implements GraphService { @Autowired
private NebulaSessionFactory sessionFactory; @Override
public <T> NebulaResult<T> query(String graphSpace, String gql) {
Session session = null;
try {
log.info("GQL: {}", gql);
session = sessionFactory.getSession();
NebulaResult<Void> res = query(session, "USE " + graphSpace);
if (!res.isSuccess() || res.getResults() == null || res.getResults().size() == 0) {
log.error("Failed to use space:{}", graphSpace);
return null;
}
if (!graphSpace.equals(res.getResults().get(0).getSpaceName())) {
log.error("Failed to use space:{}, result:{}", graphSpace, res.getResults().get(0).getSpaceName());
return null;
}
return query(session, gql);
} catch (IOErrorException e) {
log.error(e.getMessage(), e);
return null;
} finally {
if (session != null) {
session.release();
}
}
} private <T> NebulaResult<T> query(Session session, String gql) throws IOErrorException {
String json = session.executeJson(gql);
return JacksonUtil.extractByType(json, new TypeReference<>() {});
}
}

辅助类 NebulaResult.java 等

外层结构

这里定义了 json 格式响应的外层结构

@Data
public class NebulaResult<T> implements Serializable {
private List<Error> errors;
private List<Result<T>> results; @JsonIgnore
public boolean isSuccess() {
return (errors != null && errors.size() == 1 && errors.get(0).getCode() == 0);
} @Data
public static class Error implements Serializable {
private int code;
} @Data
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Result<T> implements Serializable {
private String spaceName;
private List<Element<T>> data;
private List<String> columns;
private Error errors;
private long latencyInUs;
} @Data
public static class Element<T> implements Serializable {
private List<Meta<T>> meta;
private List<Serializable> row;
} @Data
public static class Meta<T> implements Serializable {
private String type;
private T id;
}
}

内层因为区分Edge和Vertex, 结构不一样. 如果是混合返回的结果, 可以用 Serializable

String gql = "match (v:ADDR)-[e]-() where id(v)==\"ADD:123123\" return v,e limit 100";
NebulaResult<Serializable> res = graphService.query("insurance", gql);
log.info(JacksonUtil.compress(res));
Assertions.assertThat(res).isNotNull();

对于边, 需要使用结构化的ID

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EdgeId implements Serializable {
private int ranking;
private int type;
private String dst;
private String src;
private String name;
}

用这个结构进行查询

NebulaResult<EdgeId> res3 = graphService.query("t_test1", "MATCH ()-[e:follow]-() RETURN e");

对于点, ID就是String

NebulaResult<String> res2 = graphService.query("t_test1", "MATCH (v:player) RETURN v");

Nebula Graph介绍和SpringBoot环境连接和查询的更多相关文章

  1. 使用图数据库 Nebula Graph 数据导入快速体验知识图谱 OwnThink

    前言 本文由 Nebula Graph 实习生@王杰贡献. 最近 @Yener 开源了史上最大规模的中文知识图谱--OwnThink(链接:https://github.com/ownthink/Kn ...

  2. 使用 Docker 构建 Nebula Graph 源码

    Nebula Graph 介绍 Nebula Graph 是开源的高性能分布式图数据库.项目使用 C++ 语言开发,cmake 工具构建.其中两个重要的依赖是 Facebook 的 Thrift RP ...

  3. 使用图数据库 Nebula Graph 数据导入快速体验知识图谱

    本文由 Nebula Graph 实习生@王杰贡献. 最近 @Yener 开源了史上最大规模的中文知识图谱——OwnThink(链接:https://github.com/ownthink/Knowl ...

  4. 本文介绍如何使用 Docker Swarm 来部署 Nebula Graph 集群,并部署客户端负载均衡和高可用

    本文作者系:视野金服工程师 | 吴海胜 首发于 Nebula Graph 论坛:https://discuss.nebula-graph.com.cn/t/topic/1388 一.前言 本文介绍如何 ...

  5. Nebula Graph 技术总监陈恒:图数据库怎么和深度学习框架进行结合?

    引子 Nebula Graph 的技术总监在 09.24 - 09.30 期间同开源中国·高手问答的小伙伴们以「图数据库的设计和实践」为切入点展开讨论,包括:「图数据库的存储设计」.「图数据库的计算设 ...

  6. 用Docker swarm快速部署Nebula Graph集群

    用Docker swarm快速部署Nebula Graph集群 一.前言 本文介绍如何使用 Docker Swarm 来部署 Nebula Graph 集群. 二.nebula集群搭建 2.1 环境准 ...

  7. Nebula Graph 在微众银行数据治理业务的实践

    本文为微众银行大数据平台:周可在 nMeetup 深圳场的演讲这里文字稿,演讲视频参见:B站 自我介绍下,我是微众银行大数据平台的工程师:周可,今天给大家分享一下 Nebula Graph 在微众银行 ...

  8. Neo4j 导入 Nebula Graph 的实践总结

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

  9. Nebula Graph 的 Ansible 实践

    本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow & 看大厂图数据库技术实践 背景 在 Nebula-Graph 的日常测试中,我们会经常在 ...

随机推荐

  1. 前端监控系列2 |聊聊 JS 错误监控那些事儿

    作者:彭莉,火山引擎 APM 研发工程师.2020年加入字节,负责前端监控 SDK 的开发维护.平台数据消费的探索和落地. 有必要针对 JS 错误做监控吗? 我们可以先假设不对 JS 错误做监控,试想 ...

  2. [跨数据库、微服务] FreeSql 分布式事务 TCC/Saga 编排重要性

    前言 FreeSql 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/Gbase/神通/人大金仓/翰高/Clickhouse/MsAcc ...

  3. 如何快速上手AIRIOT?

    AIRIOT物联网低代码平台,快速构建稳定可靠的物联网系统,丰富的功能库及组件库,具备低成本.高效率.易操作,可扩展等特点,节省物联网项目实施时间及人力成本,支持二次开发.   [六步快速上手,玩儿转 ...

  4. SecureCRT 和 Xshell 连接ENSP 教程

    前言:很多人问我说想尝试使用 CRT和Xshell连接 ENSP的某台设备,以模拟现网中的工作状态,所以出了这篇随笔. ENSP版本: Xshell 连接教程 Xshell 7 评估版(其他版本没测试 ...

  5. TMC2209步进驱动芯片基本配置及使用

    TMC2209是用于两相步进电机的超静音电机驱动器IC. TMC2209与许多传统驱动器以及TMC2208引脚兼容. TRINAMICs先进的StealthChop2斩波器可确保电机无噪音运行,实现最 ...

  6. 使用STM32控制TMC5160驱动步进电机

    首先先来了解一下TMC5160的3种工作模式 TMC5160通过两个引脚来控制它的工作模式:SD_MODE和SPI_MODE. 1.当SD_MODE接地,SPI_MODE拉高,TMC5160即工作在模 ...

  7. Node.js躬行记(22)——Node环境升级日志

    公司之前所有的 Node 项目,其环境都是 8.9.4 版本,发布于 2018 年的一个比较古老的版本. 老版本有两个比较明显的问题: Node 高版本的特性和方法都无法使用. 有些第三方新版本的包无 ...

  8. 手写tomcat——编写一个提供servlet能力的 http服务器

    点击查看代码 package com.grady.diytomcat; import com.grady.diytomcat.handler.RequestHandler; import org.do ...

  9. IIS 实现http重定向https(亲测有效:解决URL重写模块配置https重定向不生效的问题)

    前言 以前部署网站的时候,都是通过代码来实现http重定向https,最近在部署个人网站的时候,突发奇想可不可通过IIS来实现无代码的重定向呢? 在一番操作猛如虎的搜索引擎操作后,发现只有google ...

  10. 对比es6class类和构造函数

    构造函数 在原来class 类这个语法糖没有出来之前 我们一般会把方法挂在prototype 上 为了防止过多的开辟内存 1 // 构造函数------------------------------ ...