随着 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 拉你进交流群~~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。