V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NebulaGraph
V2EX  ›  推广

Nebula Storage 2.0 存储格式

  •  
  •   NebulaGraph · 2021-03-10 11:30:11 +08:00 · 1121 次点击
    这是一个创建于 1358 天前的主题,其中的信息可能已经有所发展或是发生改变。

    storage-format-in-nebula-graph-2.0

    随着 2.0 各版本的陆续发布,Nebula Graph 迎来了一系列的改动,在存储方面,影响最大的改动就是底层编码格式进行了修改。Nebula Graph 的底层存储是基于 KV 保存在 RocksDB 中,本文将介绍新老编码格式的差异,以及为什么要修改存储格式等一系列问题。

    1.0 版本的格式

    我们先简单回顾下 1.0 版本的编码格式,不熟悉的可以参考这篇博客《 Nebula 架构剖析系列(一)图数据库的存储设计》。由于在 1.0 版本中,点的 ID 只能够用整型来表示,所以底层所有 VertexID 都是以 int64 来保存的。

    • 点的格式

    Nebula Graph 1.0 点的格式

    • 边的格式

    Nebula Graph 1.0 边的格式

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

    rocksdb key 存储

    这个格式能够满足 1.0 绝大多数接口的需要,比如 fetchgo 都只需要指定对应前缀,就能获取对应数据。

    2.0 版本的格式

    在 GA 之前发布的版本,底层存储格式其实和 1.0 是基本相同的。如果 VertexID 是整型,和 1.0 格式完全一致。而如果 VertexID 类型支持 string,则从占用 8 个字节的 int64 改成了固定长度的 FIXED_STRING,长度需要用户在 create space 时候指定长度。对于不足的长度系统自动使用 \0 补齐,而超过指定长度的 VertexID 会直接报错。

    在 GA 版本中,我们对底层存储格式进行了若干改动,因此这次版本升级时需要通过升级工具,将原有格式的数据转换为新格式的数据。如下是在 2.0 GA 版本中采用的存储格式。

    2.0 版本存储格式

    • 点的格式

    Nebula Graph 2.0 点的格式

    • 边的格式

    Nebula Graph 2.0 边的格式

    和 1.0 存储格式对比

    点格式版本对比

    点格式版本对比

    其中有几个比较大的改动:

    1. VertexID 的长度如前文所说,从固定的 8 字节,修改为 n 个字节。VertexID 类型为整型时,n 为 8,VertexID 类型为 string 类型时,n 为指定长度。
    2. 点去掉了 1.0 的时间戳。边将 1.0 时间戳改为了一个字节的占位符。
    3. 对于点和边的第一个字节,不再使用同一个 Type,在物理上点和边进行了分离。

    这些改动主要是基于以下几点进行考虑的:

    1. VertexID 改变主要是为了支持 string ID 同时兼容 1.0 版本 int ID 。在 storage 中,把 VertexID 都处理为 bytes,只在返回结果时根据 space 的设置不同,返回相应类型的 VertexID 。

      为什么 string ID 要使用 FIXED_STRING ? 如果不使用固定长度,则无法使用前缀进行扫描。通过长度不足补齐,使得所有点之间和边之间的各个前缀长度相同,从而进行相应的前缀查询。

    2. 去掉时间戳主要是因为保存多版本数据会影响性能,另外一段时间内暂时不考虑做 MVCC 的相关工作。

      在边里面还保留一个字节的占位符,主要是留给 TOSS ( transaction on storage side )使用。主要用于标识一条边的出边和入边是否完整插入了,这里不详细介绍,后续会有其他文章进行详尽的分析。

    3. 点和边分离的好处主要是能够方便快速拿某个点的所有 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 拉你进交流群~~

    推荐阅读

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5453 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 07:32 · PVG 15:32 · LAX 23:32 · JFK 02:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.