图数据库

1、为什么需要图数据库

我们假设这样一种特殊的查询场景:找出开发商是XXX,小区绿化率大于30%,周边200米有大型超市,500米有地铁,1000米有三甲医院,2000米有升学率超过60%的高中,房价在800W以内,最近被经纪人带看次数最多的房子。

这可能是一个客户想要的房子,但是各位觉得有哪个产品可以支持么?

如果说我们用传统的关系型数据库,MySQL或者Oracle可以吗?那是不是我们要关联房源表、客户表、经纪人表、开发商表等等,一次关联几十张表才可能得到想要的结果?但明显这是不现实的;

那ES ( Elasticsearch ) 可以吗?ES在搜索领域非常火,它可以解决吗?其实ES也是解决不了的,ES要搜这样的房源,肯定是需要有一张很宽的房源表

那怎么搜索这套房源周边200米有大型超市?难道要建距离周边超市的距离这样一个字段吗?显然也是不现实的;

HBase更不用说了。

所以显而易见这种行业图谱的数据只能使用图数据库,比如Neo4j这样的存储引擎才可以支持。

2、图数据库

什么事图数据库

  • 不是存储图片的数据库
  • 存储节点和关系,以图结构存储和查询

用场景很广,包括:

  • 社交网络、计算机网络、道路网络、电信网络
  • 关联查询、搜索推荐
  • 风险预测、风控管理
  • 业务流程、生物流程
  • 公司或市场结构
  • 事件及其之间的因果关系或其他联系

图数据库

3、图数据库技术选型

主要关注一下几个方面:

  • 开源
  • 成熟度
  • 可扩展
  • 文档丰富度
  • 性能
  • 稳定性
  • 运维成本
  • 易用性

前面看到图数据库虽然有很多种,但实际上开源的、流行的图数据库就只有以下几种:

  • Neo4j
  • OrientDB
  • ArangoDB
  • JanusGraph
  • Dgraph

Neo4j实际上是用来做对比的

OrientDB和ArangoDB都是老牌的图数据库了,发展比较早,从2012、2013年就开始做了

JanusGraph和Dgraph是比较新的,从2016、2017年才开始做。

3.1 主流图数据库对比

图数据库

Neo4j历史悠久,且长期处于图数据库领域的龙头地位,那为什么不考虑它呢?原因很简单,因为它开源的社区版本只支持单机,不支持分布式

OrientDB和ArangoDB它们起步比较早,最初的时候都是一个单机的图数据库,然后随着用户数据量的不断增加,后期增加了分布式模式,支持集群和副本,但是经过调研发现,可能是由于后加的功能,他们的分布式支持的不是很好。

所以主要注意力放到了JanusGraph和Dgraph上,他们发展的比较晚,从设计之初就考虑了分布式和扩展性,所以对分布式支持的非常好,也都是完全的开源免费,存储数据模型也都是专为图数据而设计。

他们有一个比较大的区别就是,JanusGraph的存储需要依赖于其他存储系统,而Dgraph使用自身的存储系统,这就造成了前面提到的运维成本的问题。

例如:JanusGraph多数使用HBase作为底层存储系统,而HBase又依赖于Zookeeper和HDFS,另外JanusGraph的索引又依赖于ES,所以想要搭建一套完整的JanusGraph,需要同时搭建维护好几套系统,维护成本非常大;而Dgraph这些都是原生支持的,所以相对来说,Dgraph维护成本低很多

3.2 JanusGraph架构

图数据库

JanusGraph的存储系统依赖于像Cassandra、HBase、BerkelyDB等等这样的存储系统,索引系统依赖于Elasticsearch、Solr、Lucene等等;

基于这些原因,它和大数据生态结合的非常好,可以很好地和Spark结合做一些大型的图计算

缺点:就是它的维护成本会非常高,依赖于这么多的外部系统,搭建一套JanusGraph系统的同时需要搭建好几套依赖系统

另一方面就是稳定性,根据经验来看,系统越复杂,依赖系统越多,整体可控性就越差,稳定性风险就越大

3.3 Dgraph架构

图数据库

  • zero:集群大脑,用于控制集群,将服务器分配到一个组,并均衡数据。通过raft选主;相当于hadoop的namenode或者Elasticsearch的master。
  • alpha:存储数据并处理查询,托管谓词和索引,即datanode。
  • group:多个alpha组成一个group,数据分片存储到不同group,每个group内数据通过raft保证强一致性。
  • ratel:可视化界面,用户可通过界面来执行查询,更新或修改schema。
  • 同时Dgraph还支持gRPC或者HTTP来连接alpha进行写入或查询。

Dgraph只有一个可执行文件,通过指定不同的参数在不同的机器上启动,就能自动组成集群,无需搭建维护其他任何第三方系统,这是它的优势。

那是不是就能通过这样的架构对比,因Dgraph运维简单就直接选择它呢?

肯定不行,我们还需要做一些性能压测来对比,如果说JanusGraph的性能是Dgraph的好几倍,那维护成本高些也是可以接受的。所以基于这个目的,我们对这两个图数据库做了详细的性能对比测试。

3.4 JanusGraph和Dgraph性能对比

图数据库

可以看出Dgraph相对JanusGraph的查询性能的优势是非常大的。

3.5 JanusGraph vs Dgraph

图数据库

4、Dgraph原理

图数据库

  • 存储引擎

Dgraph的存储引擎是自研的Badger,完全由Go语言开发。

最初Dgraph存储也是使用RocksDB,但是后来上层通过GO调用,出现内存溢出问题,于是Dgraph团队就用Go实现了一个高效的、持久化的,基于LSM的键值数据库,并且号称随机读比RocksDb快3.5倍

  • 存储结构(因为存储引擎是KV,所以存储结构也是KV)

(Predicate, Subject) --> [sorted list of ValueId],Key是由谓词和主语组成的,Value是一个有序的数组。

例如:

(friend, me)-->[person1, person2, person3, person4, person5],

Key是friend和me,friend是关系,me是主语,这样组成的一个Key;

Value是有序的,me的所有friend,从person1、person2到person5这些ID组成的一个有序的数组

基于这样的底层存储结构设计,Dgraph同一个谓词下的所有数据都存储在同一个数据结点甚至同一个数据块中,所以这样查询一个谓词数据时候,只需要一次RPC调用就可以拿到这个谓词下面全部需要的数据,对于后面的一度、二度、多度的关联查询有非常大的性能提升,这是它核心的优势。

  • 数据分片(作为一个分布式系统,要想平滑的扩展,必须要支持数据分片)

根据谓词分片,相同谓词的数据按序存储在同一个节点,减少RPC,提升查询性能,不同谓词可能是在不同的节点

定期数据均衡(rebalance-interal),zero节点会定期的检测各个节点的数据是否均衡,如果某个节点数据过大或者过小,会导致查询的性能下降,因此zero节点会尽量在保证每个节点的数据均衡

group根据replicas和alpha启动顺序确定,因为Dgraph的副本一致性是依赖Raft协议,所以要保证至少三个节点,才能保证数据的强一致性

  • 高可用

每个group至少3个alpha,互为副本,raft协议保证强一致性;每个group中的alpha的数据保持一致,这样某个alpha节点挂了,可以通过其他的alpha进行数据恢复

write-ahead logs,预写日志;分布式很常见的WAL机制,为了提升写入性能,一定是先写缓存后刷磁盘的,不会直接写磁盘的,那样性能会非常低,但先写内存后写磁盘会带来一个问题,一旦机器挂掉了,内存数据没有刷到磁盘中,那这部分数据就会丢失。因此大部分分布式系统,

比如:HBase、Elasticsearch、Dgraph等都是数据写内存之前,先预写日志,日志会实时刷到磁盘上,然后再将数据写内存,一旦内存中数据丢失了,可以通过磁盘上的日志回放这些数据,从而保证高可用性

5、Bulk Loader优化

图数据库

其实我们对于Dgraph的研究也仅仅只有几个月而已,所以目前只是做了一些小的优化:480亿的行业图谱如何快速的导入到集群中?

最开始使用Java客户端写入,发现这种方法性能非常低,完全写完可能需要整整一周的时间;

然后使用Dgraph的Bulk Loader写入,先生成索引数据,再通过alpha节点加载,最后启动集群来提供服务,这种方式需要48小时才能全部写入完成,时间也有点长,是否还能进一步优化提升速度呢?

于是我们研究了一下Bulk Loader的源码,发现只是一个简单的Map Reduce过程,但他是在单机上执行的,使用单机执行是因为它要分配一个全局唯一的UID,为了保证UID的唯一性和顺序性而选择单机执行,使用单机多线程,启动多个Map和Reduce线程,然后每个线程生成Shard文件,最后通过Dgraph的alpha加载数据。于是基于对源码的理解,我们发现是可以优化的,Dgraph原本作为分布式系统,各种查询写入都是可以做线性扩展的,不能说最初的批量导入只能是一个单机的模块。

所以我们对源码进行了一定的优化,将原来的单机多线程改为了多机多线程模式,首先通过Partition模块,为原来的每条数据分配一个UID,这块还是单机执行的,把相同group的数据分到一个数据块中;然后把这些数据块分发到不同机器上,每台机器上都可以启动原来的Map进程和Reduce进程,每台机器都可以生成Dgraph需要的数据文件,再在每台机器上启动alpha进程加载这些数据文件,直至整个集群启动成功为止。这样把480亿三元组的数据初始化导入从48小时提升到了15小时,提升了三倍性能。

6、Dgraph不足

图数据库

那Dgraph性能这么好,运维又简单,是不是就可以说Dgraph是一个完美的图数据库呢?是不是所有的场景都可以用Dgraph来支持呢?显然不是的,没有最完美的系统,只有最适合你业务的系统;就像没有最完美的人,只有最适合你的人一样。Dgraph也是有它的缺陷和不足的:

  • 不支持多重边

就是说任意一对顶点,相同标签类型的边只允许存在一条;在JanusGraph中,两个顶点确定之后,是允许存在多重边的。比如:Dgraph中,我和你是同学关系,那只能有一条叫同学关系的边;但在JanusGraph中,我和你可以同时是小学同学、中学同学、大学同学,有三条同学关系的边。

  • 一个集群只支持一个图

目前Dgraph一个集群只支持一个图,支持多图这个功能官方正在开发中,后期会支持;目前对贝壳的影响还不大,贝壳的图谱都是比较大的、隔离的,比如行业图谱480亿本身就是需要一个单独的集群的,不会和其他图谱共用,目前还够不成太大的问题。后期自然是希望官方可以尽快的支持了。

  • 大数据生态兼容不够

不像JanusGraph和大数据生态兼容的那么好,因为JanusGraph本身就是基于HBase存储的;Dgraph本身使用Go开发,使用Spark对它进行大并发写的时候,会出现overload的状态。

  • 不是很成熟

Dgraph从2016年开始做,总结下来并不是很成熟,有很多小问题,但是更新也比较快,很多问题很快就修复了。

结一下,就是没有最完美的系统,只有最合适的系统

图数据库

上一篇:mysql的那点事儿,你知道吗


下一篇:《SQLite3 — 运算符》