一文带你了解 「图数据库」Nebula 的存储设计和思考( 五 )


追问:点没有事务吗?
是这样 , 因为点是只存了一份 , 所以它是不需要事务的 。一般来说 , 问这个问题的人是想强调点和边之间的事务 , 像插入边时看点是否存在 , 或者删除点时删除对应边 。目前 Nebula 的悬挂点的设计是出于性能上的考虑 。如果要解决上面的问题的话 , 会引入完整的事务 , 但这样性能会有个数量级的递减 。顺便提下 , 刚说到 TOSS 是链式形式同步信息 , 上面也提到能这样做的原因是因为第一个节点能完整拼出第二个节点的数据 。但链式的话对完整的事务而言 , 性能下降会更严重 , 所以未来事务这块的设计不会采纳这种方式 。
数据膨胀问题
首次导入数据是怎么存储的 , 因为我发现首次导入数据磁盘占用会较多?
大家发现如果磁盘占用高 , 一般来说是 WAL 文件比较多 。因为我们导入的数据量一般比较大 , 这会产生大量的 wal , 在 Nebula 中默认的 wal ttl 是 4 个小时 , 在这 4 个小时中系统的 WAL 日志是完全不会删除的 , 这就导致占用的磁盘空间会非常大 。此外 , RocksDB 中也会写入一份数据 , 相比后续集群正常运行一段时间 , 这时候磁盘占用会很高 。对应的解决方法也比较简单 , 导入数据时调小 wal ttl 时间 , 比如只存半小时或者一个小时 , 这样磁盘占用率就会减少 。当然磁盘空间够大你不做任何处理使用默认 4 小时也 ok 。因为过了若干个小时后 , 有一个背景线程会不断去检查哪些 wal 可以删掉了 , 比如说默认值 4 个小时之后 , 一旦发现时过期的 wal 系统便会删掉 。
除了初次导入会有个峰值之外 , 线上业务实时写入数据量并不会很大 , wal 文件也相对小 。这里不建议手动删 wal 文件 , 因为可能会出问题正常按照 ttl 来自动删除就行 。
compact 都做了什么事可以提高查询 , 也减小了数据存储占用? 可以看下 RocksDB 介绍和文章 , 简单说下 Compaction 主要是多路归并排序 。RocksDB 是 LSM-Tree 树结构 , 写入是 append-only 只会追加地写 , 这会导致数据存在一定的冗余 。Compaction 就是用来降低这种冗余 , 以 sst 作为输入 , 进行归并排序和去除冗余数据 , 最后再输出一些 sst 。在这个输入输出过程中 , Compaction 会检查同一个 key 是否出现在 LSM 中的不同层 , 如果同一个 key 出现了多次会只保留最新的 key , 老 key 删掉 , 这样提高了 sst 有序的程度 , 同时 sst 数量和 LSM-Tree 的层数可能会减小 , 这样查询时候需要读取的 sst 数量就会减少 , 提高查询效率 。
磁盘容量本身不均怎么处理
不同大小的磁盘是否考虑按百分比占用 , 因为我使用两块不同大小的磁盘 , 一块占满之后导数就出现问题了
目前是不太好做 , 主要原因是存储 partition 分布查找是按照轮循形式进行的 , 另外一个原因是 Nebula 进行 Hash 分片 , 各个数据盘数据存储大小趋近 。这会导致如果两个数据盘大小不一致 , 一个盘先满了后面的数据就写入不进去 。解决方法可以从系统层进行处理 , 直接把两块盘绑成同一块盘 , 以同样一个路径挂载 。
Nebula 的 RocksDB “魔改”
Nebula 的 RocksDB 存储中 , 是通过列 column family 来区别 vertex 属性吗?
目前来说 , 其实我们完全没有用 column family , 只用了default column family 。后续可能会用 , 但是不会用来区分 vertex 属性 , 而是把不同 partition 数据分到不同 column family , 这样的好处是直接物理隔离 。
Nebula 的魔改 wal 好像是全局 multi-raft 的 wal , 但是在目录上体现出来的好像每个图空间都是单独的 wal , 这个原理是啥? 首先 , Nebula 的确是 multi-raft , 但没有全局 wal 的概念 。Nebula 的 wal 是针对 partition 级别的 , 每个 partition 有自己的 wal , 并不存在 space 的 wal 。至于为啥这么设计 , 相对来说现在实现方式比较容易 , 虽然会存在性能损耗 , 像多个 wal 的话磁盘写入就是个随机写入 。但是对 raft 而言 , 写入瓶颈并不是在这而是系统的网络开销 , 用户的复制操作 replication 开销是最大的 。


推荐阅读