系列文章

  1. DGraph 源码阅读 (1) - 架构简介
  2. DGraph 源码阅读 (2) - Raft
  3. DGraph 源码阅读 (3) - Mutation
  4. DGraph 源码阅读 (4) - Query

DGraph 是一个开源的分布式图数据库。这个项目很早以前就开始了,因为最近 A 轮融了 11.5M 的美金上了 HN,所以才注意到。Github 上已经有 10000+ 的 star 了,相比之下,相信更多人知道的 Neo4J 才 6000 多的 star。

DGraph 的官网宣称了它相比 Neo4J 的优势在于可以支持 sharding,进而可以水平扩容。而 Neo4J 只有企业版才支持集群部署,并且每个节点都是完整的 replica,数据量一大,只能做垂直扩容。同时也是因为有 Sharding,虽然 Neo4J 也支持 ACID,DGraph 支持 ACID 就更加有技术含量了。

接下来进入正题。这篇文章主要介绍一下 DGraph 的各个模块。

基本结构

一个 DGraph 的完整部署包含了两个部分:

  1. Zero 节点
  2. Alpha 节点

处理以上两个部分之外,还有可选的 ratel 组件。它是一个 DGraph 的 Web UI,可以执行 Query 以及查看 Schema。

Zero 节点

Zero 节点用于管理集群的状态。为了保证可用性,一般可以部署 3 个以上的节点。Zero 节点会组成一个 Raft Group 来保证对于集群状态的 Consensus。其中一个节点是 Raft 的 Leader。

Alpha 节点

Alpha 节点是实际存储数据和执行请求的节点,可以水平扩展。实际的数据存储使用了 Badger 系统,是一个 KV 数据库。

基本概念

和大部分图数据库一样,DGraph 也继承了很多概念,比如 Subject,Predicate 和 Object 等等。这些图数据库的基本概念这里就不多做介绍了,有兴趣的可以自行查阅。

除了图数据库的概念之外,还有一些 Raft 相关的概念,比如 Raft Leader 和 Raft Group。Leader 这里不做解释。Group 是指 Raft 的一个集群,在 TiKV 等分布式数据库中也有使用,接下来详细介绍一下 DGraph 如何分配 Raft Group。

Raft Group

首先之所以要分割为若干个 Raft Group 是因为一个 Raft 集群不适宜有很多个节点。这是因为节点数量一多,构成 Quorum 的数量就会变多,达成一致的开销就越大。实践中,往往一个 Raft 集群只会包含 3 - 5 个节点,即保证了可以接受的可用性,同时性能也不至于太差。

既然一个 Raft Group 的节点不能太多,那么如果只用一个 Group 来管理所有的数据,意味着整个集群的节点数量不能太多,即使数据很大,这样也就没法做到水平扩容了。为了解决这个问题,DGraph 将数据 Shard 成多份,每一份有一个 Raft Group 管理。那么整个 DGraph 集群就会有多个 Raft Group。所以 DGraph 中 Alpha 节点和 Raft Group 的关系如下:

  1. 一个 Alpha 节点同时是多个 Raft Group 的成员,它会同时 serve 多个 shard 的数据(此处存疑,看代码好像是一个 Alpha 节点只对应一个 Raft Group)。
  2. 一个 Raft Group 中的成员不会同时出现在一个 Alpha 节点上,这是为了保证可用性。

Sharding 方式

虽然这不是一个基本概念,但是和 Raft Group 的定义相关,所以也放在这里。

截止到 1.0.16 版本,DGraph 还是以 Predicate 为单位来 Shard 数据。也就是说,一个 Predicate(比如 name)对应的所有边,一定都在一个 group 中,这些边构成一个 Tablet(一个 group 中会有多个 tablet)。这种 Sharding 方式即有优点又有缺点。

优点

  1. Routing 更加简单。如果要遍历一条边,只需要访问对应 group 中的一个节点即可。

缺点

  1. 在某一个 Predicate 的数据量很大的时候,没法再继续分割。这样导致那个节点的 load 会很大。官方文档里提到说以后要改成一个 Predicate 也可以拆分,不知道怎么处理 routing 的问题。

WAL

因为底层使用的 Badger 中要保证有序,如果每次操作都落盘并排序,性能会受到很大的影响,所以写操作会停留在内存中,定期刷到磁盘。为了保证这些写操作被持久化,DGraph 使用 WAL(i.e. Write Ahead Log) 来保证。同时,在响应读操作的时候,WAL 对用的 mutation 也需要被 apply 到返回结果上,这个和 LSMT 很像。

ACID 支持

DGraph 作为一个分布式的图数据库,支持 ACID 的事务。

Isolation

它提供了 Snapshot Isolation 的隔离性。具体实现是通过 Zero 集群来分配单调增的时间戳来实现的。为了保证性能,会一次分配多个时间戳。

Atomic

原子性也是通过类似的方式。一个 Transaction 中的操作会被标记为同一个时间戳,这样就可以做到 all-or-nothing。