一篇看懂图数据库janusgraph存储结构

简介

JanusGraph是以邻接列表存储的,这意味着图形存储为邻接列表的顶点集合。顶点的邻接列表包含全部点的边(出边和入边,包含边的属性)和顶点对应的属性。 JanusGraph按排序顺序维护邻接列表中的每一个顶点,其顺序由排序键和边标签的排序顺序定义。排序顺序容许使用以顶点为中心的索引有效地检索邻接列表的子集。 JanusGraph图形的邻接列表能够存储在支持Bigtable数据模型的任何存储后端中。 对于图形的存储,对于图的拆分格式有两种方式:按点切割、按边切割。html

  • 按点切割:根据点进行切割,每一个边只存储一次
  • 按边切割:根据边进行切割,以节点为中心,边会存储两次,JanusGraph就是采用的按边切割,按边切割后以邻接列表的形式存储图形。

经过以邻接列表格式存储图形,JanusGraph确保全部顶点的边和属性紧密地存储在存储后端中(好比Hbase\Cassandra等),从而能够加速遍历。缺点是每一个边必须存储两次一次做为source顶点的出边被存储,一次做为target顶点入边被存储,固然这也使咱们能够在source Vertex和target Vertex中任意顶点均可以快速找到对端。java

Bigtable数据模型:

来自官网的示意图: sql

在这里插入图片描述
在Bigtable数据模型中,每一个表是行的集合,由一个key惟一标识。每行由任意(能够很大数量可是必须有限数量)数量的cell组成。cell由column和value组成。cell由给定行内的column惟一标识。 cell(单元格)、column(列)、value(列值) Bigtable模型中的行称为“宽行”,由于它们支持大量cell,而且没必要像关系数据库中那样预先定义这些cell的column。在关系型数据库中咱们必须先定义好表的schema,才能够存储数据,若是存储过程当中想要改变表结构,则全部的数据都要对变化的列作出变化。可是Bigtable模型存储中就没必要如此,每一个行的column不一样,咱们能够随时仅对某一行进行变化,也不准预先定义行的schema,只须要定义图的schema便可。 JanusGraph对Bigtable数据模型有一个额外要求:存储边的单元格必须按column排序,而且列范围指定的单元格子集必须是有效可检索的(例如,经过使用索引结构,跳过列表或进行二进制搜索)。 此外,特定的Bigtable实现可使行按其键的顺序排序。JanusGraph能够利用这样的键序来有效地划分图形,从而为很是大的图形提供更好的加载和遍历性能。可是,这并非必需的。

JanusGraph的存储:

来自官网的示意图: 数据库

在这里插入图片描述
前面说过,JanusGraph使用Bigtable模型进行存储数据,若是使用的存储后台支持键顺序(如Hbase),则邻接列表将按Vertex Id排序进行顺序存储的。 在JanusGraph存储数据的表中,行的惟一key能够是任意字符串(目前最大为64KB,尽管用户大多数只使用10-100字节,下面介绍了vertex id的组成)。每次在一行中读或写数据都是一个原子操做(尽管一行中不一样列可能正在进行读或写),这个设计使客户端能够更加方便的推导出在并发更新相同行时的系统行为。 在JanusGraph中,是点为中心,按切边的方式存储数据的。好比在Hbase中节点的ID做为HBase的Rowkey,节点上的每个属性和每一条边,做为该Rowkey的一个个独立的Cell。即每个属性、每一条边,都是一个个独立的KCV结构(Key-Column-Value)。

1:具体案例

好比咱们下面有一个图, 后端

在这里插入图片描述
拆分定义后为:
在这里插入图片描述
而后其存储格式就基本就能够肯定了。

2:vertex id的组成:

  1. Vertex ID以Rowkey的形式存储在HBase中,Vertex ID共包含64个bit。
  2. Vertex ID由partition id、count、ID padding三部分组成。 2. Vertex ID由partition id、count、ID padding三部分组成。
  3. 最高位5个bit是partition id。partition是JanusGraph抽象出的一个概念。当Storage Backend是HBase时,JanusGraph会根据partition数量,自动计算并配置各个HBase Region的split key,从而将各个partition均匀映射到HBase的多个Region中。而后经过均匀分配partition id最终实现数据均匀打散到Storage Backend的多台机器中。
  4. 中间的count部分是流水号,其中最高位比特固定为0.
  5. 最后几个bit是ID padding, 表示Vertex的类型。具体的位数长度根据不一样的Vertex类型而不一样。最经常使用的普通Vertex,其值为'000'。

其组成图为(腾讯云社区): 数组

在这里插入图片描述

3:边和属性在cell中具体的存储形式

下面来自官网的示意图: bash

在这里插入图片描述

深蓝色框表示使用可变长度编码方案编码的数字,以减小它们消耗的字节数。红框表示使用关联属性键中引用压缩元数据序列化的一个或多个属性值(即对象)。灰色框表示未压缩的属性值(即序列化对象)数据结构

上图中咱们能够看出,Edge和Property在cell中都是由column(列)和value(值)组成。 Edge中column由labelid(边标签id)+direction(边的方向,相对于节点的出边或者入边)+sort key(用于边排序的key)+adjacent vertex id(临近顶点的id)+edge id(边id)组成,value由signature key(签名密匙)+other properties(边的其余属性)组成。并发

注意:此处的组成元素SortKey是一种特殊的属性,JanusGraph容许在定义Edge Label时指定其中的一个或多个属性为Sort Key,主要的做用是:将数据序列化进行存储时,序列化中edge会根据你设定的sort key进行排序,好比A有多个朋友关系的edge,每个edge都有一个创建时间(createtime),sort key即可以在存储时将边按照创建时间进行顺序存储,这样便于查找某个createtime的边,也便于范围查找。post

此处的signature key观看源码其实就是一个list数组, 里面存储的是边的property的key id(注意不是property id),做用是:边的other properties是被序列化存储在磁盘中,当咱们查找 边是否包含某一属性时不可能将其序列化回来再进行查找,这时候signature key的做用就体现出来了,经过其就能够知道这条边有什么属性,就能够更快的进行查找。如今再看‘红框表示使用关联属性键中引用压缩元数据序列化的一个或多个属性值’这句话,说的就是我引用属性的key id 。 signature key有一种空间的换时间的感受。。

Property中column由key id(属性的键id)组成,value由属性id+属性值组成。

这里注意key id 和 property id,key id 是属性key的id,举个栗子:name:李阳,这里的key id就是name这个property key的id,而name:李阳总体有一个id就是property id 了

4:其中对于property的存储:

一个Property Key所关联的属性值有可能有一个,也有可能有多个,所以,JanusGraph使用Cardinality来描述Property Key的这种特色。有SINGLE,LIST和SET三种类型, 源码中的PropertyKey接口能够看到:

public interface PropertyKey extends RelationType {
    Class<?> dataType();
    Cardinality cardinality();
}
复制代码
  • SINGLE表示一个Property Key只对应一个Value,这是最经常使用的场景。

Cardinality为SINGLE时的存储结构,HBase的列名只存储Property Key的ID。具体的Property Value值以及Property ID(JanusGraph为每个Property分配的惟一ID),都存放在Cell的Value中。另外,若是该Property还有额外的Remaining properties,也会放在Value中。Remaining properties通常不使用,仅在一些特殊场景下,用于为该Property记录更多的附加信息(好比存储元数据Edge Labe的定义等)。

  • LIST表示一个Property Key能够对应多个Value,多个Value能够有重复值。

Candinality为LIST时的存储结构,各个部分与Cardinality为SINGLE时的结构类似,区别在于属性的ID被放在了列名中,而不是放在Value中。

  • SET表示一个Property Key能够对应多个Value,多个Value不能够有重复值。

Candinality为SET存储结构,各个部分与Cardinality为SINGLE时的结构类似,区别在于属性的值被放在了列名中,而不是放在Value中,前提是去除了重复。

JanusGraph的Property,在不一样的Cardinality下,数据存储结构略有不一样。总体原则是:根据Cardinality的不一样,列名自己可以肯定惟一的一个属性便可。

5:Edge label 的多样性

默认的多重性是MULTI

  • MULTI:容许任意一对顶点之间的同一标签具备多个边。
  • SIMPLE:在任何一对顶点之间最多容许拥有此类标签的一个边。
  • MANY2ONE:在图形中的任何顶点上最多容许此标签的一个传出边,但不对传入边施加约束。边标签mother是MANY2ONE多样性的一个例子,由于每一个人最多只有一个母亲,但母亲能够有多个孩子。
  • ONE2MANY:在图形中的任何顶点上最多容许此标签的一个传入边,但不对传出边施加约束。边标签winnerOf是ONE2MANY多样性的一个例子,由于每一个比赛最多只赢一我的,但一我的能够赢得多个比赛。
  • ONE2ONE:在图表的任何顶点上最多容许此标签的一个传入边和一个传出边。边标签结婚是ONE2ONE多样性的一个例子,由于一我的与另外一我的结婚。

Property Key和Edge Label被抽象成了Relation Type,并采用相同的数据结构。因此说 Property Key和Edge Label不能被设置为相同的名字。

6:序列化:

每一个边和属性都做为一个cell存储在其相邻顶点的行中。它们会被序列化而且column的字节顺序会遵循edge 的column中的sort key进行存储。变量id编码方案和压缩对象序列化使每一个edge/cell的存储所占空间都尽量小。 边的序列化从边标签的惟一ID开始(由JanusGraph指定)。这一般是一个小数字,而且使用变量id编码进行压缩。该id的最后一位是偏移量,用于存储这个边是入边(in)仍是出边(out)。接下来,存储包括sort key 的属性值,它是使用edge label定义的,所以排序键对象元数据能够引用边缘标签。以后,存储相邻顶点的id。JanusGraph不存储实际的顶点id,而是存储拥有此邻接列表的顶点的id。顶点id后跟此边的id,JanusGraph为每条边分配了一个惟一的id。这就是edge cell的column。edge的cell的column包含边的压缩序列化后的签名属性(由标签的签名键定义)以及未压缩序列化的边的任何其余属性。 属性的序列化表示更简单,column中只包含属性的键ID。属性id和属性值存储在value中。可是,若是将属性键定义为list()或者set(),则property id也存储在column中(上面property存储中说到过)。

JanusGraph Schema:

从上述来看,咱们能够知道,JanusGraph图的schema该怎样定义主要是由edge labels 、property keys 和vertex labels 组成(Each JanusGraph graph has a schema comprised of the edge labels, property keys, and vertex labels used therein),JanusGraph的schema能够显式或隐式建立,推荐用户采用显式定义的方式。JanusGraph的schema是能够在使用过程当中修改的,并且不会致使服务宕机,也不会拖慢查询速度。,好比一个简单的显示定义的销售图的scheme:

<propertyKey value="salesman_id" explain="销售人员id" index="true" type="java.lang.String" />
<propertyKey value="real_name" explain="姓名" index="" type="java.lang.String" />
<propertyKey value="role" explain="角色" type="" />
<propertyKey value="city_code" explain="所处城市代码" index="" type="" />
<propertyKey value="create_time" explain="建立时间" index="" type="" />

<edgeLabel value="saleman_service_for" explain="销售引导">
    <propertys>
        <property value="create_time"/>
    </propertys>
</edgeLabel>
<edgeLabel value="own_salaman_Idcard" explain="销售身份">
    <propertys>
        <property value="create_time"/>
    </propertys>
</edgeLabel>

<index elementType="vertex" indexType="compositeIndex" name="salesman_id_I" >
    <propertyKeys>
        <propertyKey value="salesman_id" />
    </propertyKeys>
</index>

<vertexLabel value="salesman" explain="销售" >
    <propertys>
        <property value="salesman_id" />
        <property value="real_name" />
        <property value="role" />
        <property value="city_code" />
    </propertys>
    <edges>
        <edge value="saleman_service_for" direction="out" />
        <edge value="own_salaman_Idcard" direction="out" />
    </edges>
</vertexLabel>
复制代码

固然,咱们也能够添加一些其余的能够组成schema的元素,上述三个是必须的,另外的好比索引(index)等,主要的结构仍是:

JanusGraph Schema
            |-----------Vertex Lables
            |-----------Property Keys
            |-----------Edge Labels
复制代码

和通关系型数据库不一样,图数据的schema是定义一张图,而非定义一个vertex的。在Mysql中,咱们一般将创建一张表定义为建立一个schema,而在JanusGraph中,一个Graph用于一个schema。

refer:博客 博客

若是转载此博文,请附上本文连接,谢谢合做~ :juejin.im/user/5c3036…

若是感受这篇文章对您有所帮助,请点击一下“喜欢”或者“关注”博主,您的喜欢和关注将是我前进的最大动力!

相关文章
相关标签/搜索