关于Dgraph切换到NebulaGraph

Nebula Graph与DGraph的两款数据库对比

本文仅限自己本地使用时的两款数据库对比,对于数据库整体性能并没有达到最佳。两款数据库均基于SSD进行优化。目前测试环境存储均为HDD,因此数据库的写入、查询均存在一定影响。

NebulaGraph官方又大量优质的此数据库与其他图数据库产品的对比文章。

1、安装

Dgraph:下载源码后解压在生产环境下即可启动图数据库

Nebula:下载deb包后安装,默认安装路径为 /user/local/nebula/。

启动数据库服务为 /usr/local/nebula/scripts/nebula.service start all

连接数据库需要使用nebula-console,进入后按照sql的基本逻辑使用

2、配置

Dgraph:

依次启动zero、alpha
--badger.vlog disk       //设置存储为磁盘存储
--abort_older_than 1m 
--pending_proposals 1000 
--lru_mb 3072            //查询的内存限制目前设置为3G,但实际使用会超出
--query_edge_limit 500000000  //设置查询边的上限


set_schema结构
task_id: [string] @index(term) .
ip_str: string @index(term) .
ip_num: string @index(term) .
scan_to: [uid] @reverse .  //reverse为可进行逆向查询
device_type: string @index(term,fulltext) @lang .
has_pid: string @index(term) .
virtual_node: [string] @index(term) . 

Nebula

在默认安装路径  /usr/local/nebula/scripts内启动
/user/local/nebula/scripts start all 即可启动数据库、存储服务

设置数据库建联执行一系列的excute,完成数据库建立
1、CREATE SPACE my_space_2(partition_num=5); 设置库名、同时设置分片数量
# 此处对比DGraph有以下几点说明
a、Dgraph同样可以设置分片,但是设置分片对内存消耗同比较大(并不准确、需要后期验证)
b、Dgraph不支持多个数据库的建立,即所有的数据存储在同一表中,当同一项目中存在需要两个不同的数据结构时,DGraph无法满足该场景

2、CREATE TAG node(ip_str string, has_pid bool default true, device_type string);
 node为节点数据类名
3、CREATE EDGE next(virtual bool default false);
 next为边数据类名,类似DGraph中的scan_to

3、两个图数据库数据结构的不同对数据库设计的影响

首先,两个图数据库的数据结构存在较大的区别。两者数据库数据结构对后续的CURD操作各有利弊。

DGraph的数据结构大致为(测试环境中的数据结构)

{
 uid: "0x1231",  //数据库生成的唯一主键,不可自定义
 scan_to: [uid1, uid2, uid3], //边关系的存储字段,将uid之间建联,uid的list只增不减
 "" : "" 其他节点属性参数
}

存储后的数据结构内容类似单个的json
{
 uid:"uid1",
 scan_to: [
 {
 uid: 'uid2',
 scan_to: [...],
virtual_node: []
 },
 {
 uid: "uid3"
 scan_to: [...],
virtual_node: []
 }
 ]
}

Nebula的数据结构大致为

节点类型:
[("100000" :node{device_type: "路由交换设备", has_pid: false, ip_str: "172.18.6.100000", task_id: "12"})]
边类型:
[:next "100000"->"100001" @0 {virtual: true}]

其中 1000000、100001均为节点的vid数据,类似于DGraph中的uid,为当前节点的唯一主键。
但是vid可以为自定义的string。node内的数据为当前的所有节点属性。
边类型中: vid1 -> vid2 为一条边的关系建联。 virtual为当前边的属性信息。
根据数据库存储的数据结构。两个数据库在存储数据时所遇到的问题
//设置一条即将入库的信息
[
 {
 'start': {
 'task_id': '1', 
 'ip_str': '172.18.0.1', 
 }, 
 'end': {
 'task_id': '1', 
 'ip_str': '172.18.0.10', 
 }
 },{...}
]

所有的插入都建立在已经完成了图数据库结构设计的前提下

即Dgraph完成了schema的创建。nebula完成了space、tag、edge的创建

Dgraph

1、由于Dgraph不支持自定义的uid,因此在存储时需要做到一下几点,才能将一条数据完整的存储在数据库中

1.检查即将入库的ip是否在图数据库中已存在(图数据库指挥根据uid判断是否存在相同数据,即使存储的内同完全一致)
2.对未入库的数据进行入库操作(同时增加IP、device_type等一系列不包含关系的数据)
3.对节点间的关系数据进行入库操作
3.1 查询start和end的ip在数据库中对应的uid信息
3.2 获取当前的scan_to字段,判断end_uid是否存在于start对应的scan_to字段内。不存在对当前的start的scan_to增加end_uid
3.3 判断start节点的 virtual_node 数据是否为0。不为0时则表示为跳跃节点,需要更改start节点信息的virtual_node字段。

2、在存储virtual_node数据时。存在节点连接状态一直变化的状态,在当前的virtual_node中就必须不断的进行增加、删除操作。但是uid的list无法进行删除操作。即当连接线为虚线后无法更改状态

3、DGraph使用事务进行图数据库的mutation,但是事务不支持锁。当不同的线程同时操作一个节点时就会造成事务冲突问题。具体可查看以下链接内容

https://discuss.dgraph.io/t/transaction-locks-and-conflicts/1982

4、DGraph存在较大概率出现OOM情况。在静置状态下。DGraph消耗的内存为

image-20210720115106501.png

当前的数据库中不存在数据。即当前数据库启动后的内存占用。当开始写入数据时,内存信息最大会维持在8G(单进程写入),但当同时查询并写入数据时,整体的内存消耗会超过10G,在测试过程中存在内存占用范围为15~20G的情况。查询较多数据量的全部数据时大概率会产生OOM。目前推测的整体占用内存较大为入库内过多的查询导致的。在每次的入库中基本过程为(查询->入库->查询->入库),因此需要大量内存。

Nebula-Graph

1、NebulaGraph的一整条插入为

1. 生成一条节点插入语句  INSERT VERTEX node(ip_str, has_pid, device_type, task_id) VALUES 
2. 生成一条边插入语句 INSERT EDGE next(virtual) VALUES 
3. 将将所有的插入语句一次性的插入到space内

// 由于可以自定义vid的内容,在插入边与插入节点时均不用提前查询唯一主键的值,后续的节点信息,在相同主键的前提下会做覆盖操作

2、有关virtual_node的添加问题

在nebula数据库中由于不支持list的数据类型,仅支持基本的数据类型。即,使用相同的task_id存储方式无法满足当前的数据库。但是同时由于task_id类别多样化的问题,可以将任务信息存储为节点并对数据进行关联,即存储的结构为。

整个数据库存在三个数据集
边数据集合、节点数据集合、任务信息数据集合

任务信息数据集      边信息数据集  任务信息数据集
    ||                 ||          ||
节点信息数据集      ==========》  节点信息数据集

3、数据库的插入语法

相比较DGraph的json格式数据插入。Nebula将数据写入到数据库语法构建过于复杂。Nebula数据库的数据插入必须依托excute()函数才能实现,即函数内为完整的NGsql语句 需要依托字符拼接完成。

4、Nebula对于内存消耗的部分信息

目标场景:在数据库中存储着60W条数据,一次查询所有的数据并返回。内存使用情况基本维持在如图所示

image-20210720192301920.png
image-20210720192721451.png
其中nebula-console为数据库操作进程,内存使用可以忽略,生产环境不会影响。

查询完成后的静息内存使用信息为

image-20210720195148386.png

数据库的查询问题

两款数据库对于查询的覆盖面基本相同,但是目前Nebula对于图数据库查询的算法覆盖不及DGraph,但目前Nebula的查询场景基本可以涵盖项目需求。以后也可以对数据库版本进行更新。

Dgraph查询结果依托特有的数据结构,本身就为标准的JSON格式,对数据的解析友好度较高。

但是Nebula查询的结果为一个ResultSet类型数据

目前需要循环遍历解析为Node类型,并单独获取单个node内的数据信息,相比Dgraph解析复杂度较大。具体流程为

1、查询一条链路消息
resp = client.execute("""GET SUBGRAPH 5 STEPS FROM '100000'""")
2、分别获取边、节点信息
node = resp.column_values('_vertices')
edge = resp.column_values('_edges')
3、遍历节点,获取节点内的全部信息
for info in node:
    new_info = info.as_list()
    if new_info:
        new_data = new_info[0].as_node()
        node_obj = new_data.propertys('node')
        node_obj['vid'] = new_data.get_id().as_string()
        print(node_obj)
4、遍历边,获取边上的全部信息
for info in edge:
    new_info = info.as_list()
    if new_info:
        new_data = new_info[0].as_relationship()
        start_id = new_data.start_vertex_id()
        end_id = new_data.end_vertex_id()

        edge_obj = new_data.propertys()
        edge_obj['source'] = start_id
        edge_obj['target'] = end_id

nebula同时提供了数据转换的函数,可以根据给出的示例,总结自己的转换函数

https://github.com/vesoft-inc/nebula-python/blob/master/example/FormatResp.py

目前对两者的数据库都只是初步使用阶段,可能存在较多不足,如在Nebula中可以将节点的其他类型实例为一种点结构数据,在建联时只需要将两种点连接就为目标点增加了一个属性,只不过属性信息存在其他点,通过边建联

不过两款图数据库都只是停留在初步阶段,正确使用仍需要以官方文档为准。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,122评论 0 4
  • 公元:2019年11月28日19时42分农历:二零一九年 十一月 初三日 戌时干支:己亥乙亥己巳甲戌当月节气:立冬...
    石放阅读 6,943评论 0 2
  • 今天上午陪老妈看病,下午健身房跑步,晚上想想今天还没有断舍离,马上做,衣架和旁边的的布衣架,一看乱乱,又想想自己是...
    影子3623253阅读 2,947评论 3 8