分布式图数据库JanusGraph初探

今天先来简单了解一下图数据库JanusGraph(janus读音类:J+nes, jay 而不是je-nes),后续简写为janus.

更新: 这篇写的比较早,很多地方不够细致,之后全局搞明白后再重写一次

0x00.What Is Graph-Database?

讲janus之前,首先得回答一下什么是图数据库? 它与传统关系型(mysql)db的区别是什么? (待补充…)

根据官方文档,janus是一个分布式图数据库引擎,具有以下特性:

  • 支持百亿级别的顶点&边的存储及查询
  • 本身不负责存储,而是借助其他存储和索引组件配合达到图形数据的查询
  • 支持事务,可并发执行复杂图操作

补充: 前身是Apache的开源项目Titan.

0x01.janus架构

了解图数据库,我打算从它的整体架构,数据结构,算法结构三个方面来学习. 首先看看它的整体架构,大致分为三部分:

  • 图计算框架(TinkerPop)
  • 数据存储(Cassandra,HBase,BerkeleyDB)
  • 索引存储(Elasticsearch,Solr,Lucene)

可以看出,janus的设计是比较模块化的,可以自行采用它支持的多种存储组合方式,以满足业务的最佳适应性.引用官方文档的图: (先需要知道OLAP,OLTP,Gremlin)

janus00

这里关于OLAP和OLTP以及Gremlin的图计算&大数据平台这不太清楚可以先跳过,后面等到学习Tinkerpop后就会了解多很多.

Gremlin

首先,大家都了解传统的sql语言,用于关系型db的各种操作.e.g (select * from stu where 1=1 or stu.name='hacker' )

再看看TinkerPop: 它制定了专用于图数据库的Gremlin语言,并定义了图中的顶点/边/属性的数据模型,并提供了以下几个重要的接口:

  • 路径分析
  • 点族群发现
  • 子图识别

并且它支持netty socket接口,通过发送gremlin请求访问服务端然后返回json数据.*其他待之后补充….*

数据存储

传统关系型db(e.g : mysql,oracle)存储是跟本身结合在一起的,但是janus是使用第三方的存储系统.待补充 (主要是说cassandra)

索引存储

传统我们了解的数据库引擎,比如mysql的innodb,myisam 等.基本使用的都是B+树的数据结构.对查询速度而言,从传统的桶排序,到hash表,到db中的index,本质都是索引的体现.

janus的索引也单独出来使用外部index system .详细待补充(主要是说Elasticsearch)

0x02.janus数据结构(待迁移至Schema篇)

看完了整体结构,再来看看janus本身作为图计算引擎的核心数据结构,包括传统图的vertex(点),edge(边)以及点边拥有的属性(支持多类型). 一般点作为一个对象,边作为一个关系.

从ds的角度来看,janus是一个邻接表方式存储的图,表中保存某个顶点的所有入射边.优点是遍历快(边和属性存储紧凑),缺点是邻接表的特点需要浪费一倍的存储.janus可与支持big table数据模型的存储后端,如图所示是bigtable(bt)数据模型:

janusDS00

由图可知,在bt下表就是很多行的集合, 并且每行都有唯一标识的key,然后每行有很多单元(cell),每个单元又由列&值组成.cell的数量可以非常大.

这里janus对bt结构有个特别要求 : cell必须通过列(column)排序,且由列执行的cell子集必须可以高效查询. (补充: 行的按key排序没强制要求,但是如果也实现了,janus遍历速度会更快)

janus实际存储则是以下图为例,key对应是vertexID,每个cell变成了这里独立的属性&边,以便更高效的插入&删除. 每行能保存的最大单元数也就 = 顶点的边数

janusDS01

那么按照这样存储的话,之前列对应的value去哪了呢? 答案是再进一层,如图所示:

janusDS02

图中三种颜色方块各自代表:

  • 初音蓝: 表示对schema编码压缩后的ID,以减少存储使用
  • 红色: 代表一个或多个属性值,这个值也会被压缩并关联到属性key上
  • 灰色: 未压缩的属性值(比如序列化后的对象)

如果是边,那么它的列实际上是由一系列属性组成的(标签ID+方向,sort key,边ID,相邻顶点ID),那么它的值是signature key+其他属性组成. (什么是签名秘钥?). 注意,从图上可知,janus并没有直接保存顶点ID ,而是保存相对邻接表的位置偏移(更省空间)

如果是属性,那么简单不少: 列是keyID,值是属性ID+属性值(这里keyID是什么?? 类似身份证号跟主键号么). 注意: 如果属性被设置为LIST,那么属性ID也会存在列中

总结:

janus的数据结构本质是来自bigtable,也就是邻接表的存储结构.由于边至少由两个顶点定义,所以肯定会被重复在邻接表中保存,存储效率会降低,但查找速率会提高.

具体的代码数据结构待补充..(java实现)

1
2


0x03.janus基本操作

1
2
3
4
5
6
7
8
9
10
//因为没有单独gremlin的语法高亮,先用java代替
//1.查询label='dev' & type='game' 的边
g.V().hasLabel('dev').has('type',within('game'))

//2.接着要看谁开发过这个game
g.V().hasLabel('dev').has('type',withiin('game')).inE().hasLabel('dev').in()

//3.接着查开发过此game的dev还开发过什么
g.V().hasLabel('dev').has('type',withiin('game')).inE().hasLabel('dev').in().outE().
hasLabel('dev').out()

可以看出,以g 对象为一张图的核心,含有核心方法V(),inE(),outE() ,has(para1,para2), hasLable(para1) ,最后根据具体查询是往里深入还是往外发散选in(),out()方法

0x04.schema

首先如果对传统db的shcema的理解就不太清楚,参考zhihu. schema和索引&视图&标的区别理解之后再看图db的. 我简单理解就是比database低一级,是表跟各种关系的集合.

这里,每个janus都会有一个核心的概念schema .不同于传统一个数据库可以有多个schema,janus中每个janus实体只有与其对应的一个schema .这个结构由下表的几种构成(不知道我能不能把它想成是索引,表,存储过程之类的?..)(以下都建立在图实例唯一):

schema name definition supported type extension
PropertyKey 顶点和边的属性.(类型需确定) String,Integer,Data,UUID,IPAdress(以及基本包装类) List,Set,Single(这是什么?)
VertexLabel 顶点类型 String
EdgeLabel 边类型 String

schema可以显式或隐式创建(推荐显式),schema可以在使用过程中修改,不会导致服务不可用,也不会降低查询速度.(怎么做到的呢? 待补充..) 补充: schema

支持的索引类型有:

等值索引(CompositeIndex): 应该是类似 1=1 and 1=2 这样的? 需指定是否唯一

混合索引(MixedIndex): 用于模糊查询 (类似like %hacker 么) 待补充….

然后以上四个scheama属性构成了SchemaVertex ,然后存储为edgestore??

后续更新 :单独谈Schema的本质与源码解读

0x05.存储结构

上面说的是janus自身的相关结构,再看看与它配对的存储和索引的结构(cassandra和elasticsearch,下面简称cd和es)

cd: 使用等值索引,其它结构不太明白

es: 使用混合索引,其它结构不太明白

图数据如何读写?

这也是关键的存储问题,图之后再补.

图写入:

  1. 生成janus对象
  2. 创建新的事务
  3. 添加点 & 边(可并行)
    • 添加点/边
    • 获取点标签/点内外?(getInVertex & getOutVertex)
    • 事务中添加点/点外添加边?(outVertex.addEdge())
    • 点/边添加属性
  4. 提交事务
  5. 遍历新增关系(addRelations) ,删除关系(deleteRelations)
  6. 封装mutator(突变?) & 下标更新(IndexUpdate)
  7. 对mutator对象操作边/下标
    • 突变边?
    • 突变下标?
    • 事务下标新增

图读取:

  1. 生成janus对象
  2. 创新新的事务
  3. 获取某个点是否拥有标签/属性(getVertex.has("name","hacker")) ,根据查询.类型以及是否有索引
    • 有索引
      1. 精准查询: 先根据cd的图下标获取边ID,然后查cd的edgeStore获取完整数据
      2. 模糊查询: 先根据ES的获取边ID,然后查cd的edgeStore获取完整数据
    • 无索引 : 需要全遍历,查询cd的edgeStore获取完整数据

0x06.可视化效果

现在不管是爬虫/数据分析还是社交网络,一般单纯说技术或者json数据是没人愿意看的.所以一般会根据数据提供一个可视化的图表,那么图数据库也有类似的可视化效果,图之后补充.