
随着 2.0 各版本的陆续发布,Nebula Graph 迎来了一系列的改动,在存储方面,影响最大的改动就是底层编码格式进行了修改。Nebula Graph 的底层存储是基于 KV 保存在 RocksDB 中,本文将介绍新老编码格式的差异,以及为什么要修改存储格式等一系列问题。
1.0 版本的格式
我们先简单回顾下 1.0 版本的编码格式,不熟悉的可以参考这篇博客《 Nebula 架构剖析系列(一)图数据库的存储设计》。由于在 1.0 版本中,点的 ID 只能够用整型来表示,所以底层所有 VertexID 都是以 int64 来保存的。
- 点的格式

- 边的格式

给定任何一个 VertexID,经过 hash,可以得到对应的 PartID,因此对于一个点和这个点的所有边(边用起点计算 hash ),都会映射到同一个分片中。需要指出的是,在 1.0 版本中,点和边的第一个字节的 Type 是相同的。也就是说,对于一个点而言,它的所有 tag 并没有在物理上连续保存,比如可能是如下保存的。对于这个 src 这个点的三个 tag ( tag1 tag2 tag3),实际上可能会被其他边隔开。

这个格式能够满足 1.0 绝大多数接口的需要,比如 fetch 和 go 都只需要指定对应前缀,就能获取对应数据。
2.0 版本的格式
在 GA 之前发布的版本,底层存储格式其实和 1.0 是基本相同的。如果 VertexID 是整型,和 1.0 格式完全一致。而如果 VertexID 类型支持 string,则从占用 8 个字节的 int64 改成了固定长度的 FIXED_STRING,长度需要用户在 create space 时候指定长度。对于不足的长度系统自动使用 \0 补齐,而超过指定长度的 VertexID 会直接报错。
在 GA 版本中,我们对底层存储格式进行了若干改动,因此这次版本升级时需要通过升级工具,将原有格式的数据转换为新格式的数据。如下是在 2.0 GA 版本中采用的存储格式。
2.0 版本存储格式
- 点的格式

- 边的格式

和 1.0 存储格式对比


其中有几个比较大的改动:
- VertexID 的长度如前文所说,从固定的 8 字节,修改为 n 个字节。VertexID 类型为整型时,n 为 8,VertexID 类型为 string 类型时,n 为指定长度。
- 点去掉了 1.0 的时间戳。边将 1.0 时间戳改为了一个字节的占位符。
- 对于点和边的第一个字节,不再使用同一个 Type,在物理上点和边进行了分离。
这些改动主要是基于以下几点进行考虑的:
-
VertexID 改变主要是为了支持 string ID 同时兼容 1.0 版本 int ID 。在 storage 中,把 VertexID 都处理为 bytes,只在返回结果时根据 space 的设置不同,返回相应类型的 VertexID 。
为什么 string ID 要使用 FIXED_STRING ? 如果不使用固定长度,则无法使用前缀进行扫描。通过长度不足补齐,使得所有点之间和边之间的各个前缀长度相同,从而进行相应的前缀查询。
-
去掉时间戳主要是因为保存多版本数据会影响性能,另外一段时间内暂时不考虑做 MVCC 的相关工作。
在边里面还保留一个字节的占位符,主要是留给 TOSS ( transaction on storage side )使用。主要用于标识一条边的出边和入边是否完整插入了,这里不详细介绍,后续会有其他文章进行详尽的分析。
-
点和边分离的好处主要是能够方便快速拿某个点的所有 tag (在 Cypher 的
MATCH语句中大量使用)。如果按原先同一Type + VertexID前缀扫描,由于点边可能掺杂在一起,会极大影响性能。而 Type 分离之后,按VertexType + VertexID前缀扫描,可以快速获取所有 tag 。在 1.0 版本中,由于没有取某个点的所有 tag 的需求,因此点和边可以按同一个前缀保存。不过在代码层面,还是有不小影响,例如 fetch 接口在 1.0 是按 VertexID 的前缀去扫描的,对于超级大点来说取 tag 性能比较差。另外如果使用 storage 提供的 scan 接口,想要获取全图的所有点,实际是扫描了整个 RocksDB 。
除了点和边的格式相关改动之外,索引的格式其实也有所改变。
一方面是 2.0 支持 NULL 后,索引也需要能够表示对应的语义。另一方面是在 1.0 的版本中,对于索引中 string 的字段的处理,实际是按变长 string 处理。因此在 LOOKUP 语句中只要使用了带 string 字段的索引,就只能使用等值查询。而在 2.0 的版本中,索引的 string 字段和数据中的 VertexID 一样,使用固定长度的 FIXED_STRING,LOOKUP 语句中带 string 字段的索引能够使用范围查询,例如 LOOKUP ON index1 WHERE col > "aaa"。有关索引部分的功能和修改,后续也会再有其他文章介绍。
以上,为本次 Nebula Storage 2.0 存储格式讲解。
喜欢这篇文章?来来来,给我们的 GitHub 点个 star 表鼓励啦~~ 🙇♂️🙇♀️ [手动跪谢]
交流图数据库技术?交个朋友,Nebula Graph 官方小助手微信:NebulaGraphbot 拉你进交流群~~