江一旺
1
背景:
全量图数据导出(我们通过此方式用来做1.0到3.6版本升级)
【nebula1.0版本】scan原理(个人理解)
通过ScanVertexProcessor.cpp和ScanEdgeProcessor.cpp源码以及nebula-client学习,其实scan就是先通过metad获取part分片的leader地址,然后使用StorageClient连接对应的leader,通过Thrift接口调用Storage服务,scan最终调用的就是ScanVertexProcessor.cpp和ScanEdgeProcessor.cpp里面的process方法,方法实现大致原理:根据传入的space、part、cursor进行rocksdb的Iterator前缀迭代操作,从cursor的位置往后面进行读取数据,其中就会判断扫描的数据类型是点还是边、是否为指定的tag或者edge,并且会判断时间是否满足要求,直到找到符合条件的数据量或者大小超过max_scan_block_size就会结束单次scan中的next操作,对于client端来说每次的next就是一次的scan操作,只不过每次scan完毕之后会回传cursor游标回来,next()的时候又会把cursor发送到nebula-storage那边。
疑问:
为什么nebula不支持同时scan点和边?如果同时支持点和边的scan感觉导出效率更高,因为指定tag或者edge就会造成没必要的磁盘数据检索。此外还可以避免点边数据量相差非常大的时候scan导致磁盘IO过高,比如:edge有100亿,vertex有只1w数据,此时去扫描vertex就会很大可能大量无效的磁盘数据加载,因为nebula服务直接定位哪条数据是点或者边的位置,只能从头到尾的进行扫描(我们线上就出现了此场景导致磁盘IO过高)
江一旺
3
感谢回复,您讲得非常有道理,但是我还是有一丢丢疑问:
1.scan 出来的点和边类型是混的,后续感觉没法用 ------这点有可能,因为点和边返回的数据结构属性不一样,但是按理可以一个reponse中包含点和边两种类型作为属性
2. 更多的场景,是指定点和边导的比较多,这样的话可能要实现两套----我感觉一套应该也能做,只不过request增加两个参数来指定特定类型点或者边应该就可以,如果这两个参数为空就代表全量扫描,不为空就扫描指定类型的点和边
可能从技术手段上是能实现,实现的代价和使用的人数不是正比,做的意义就不是很大了,我也不是太懂里面的逻辑,单纯个人理解 
关于 1,导出来的文件肯定是可以带类型的,但一个文件的类型可能有多种点、有多种边,这个后续在导入的时候,文件处理会比较麻烦。
至少现在 importer 的逻辑是指定一个csv 文件,然后在 yaml 里对这个 csv 文件进行映射配置。csv 文件里的数据都是对齐的
江一旺
5
我大概明白你的意思了,抱歉,可能前面我描述得不太清晰,其实我指的是通过spark-connector这类同步nebula的场景,导出数据直接组装成insert语句插入到另外一个nebula
1 个赞
哦哦,如果是这个场景,我觉得在 3.x是 OK 的。因为允许悬挂边的存在
不过我觉得在后续的版本里,应该还是先扫点再扫边合适。这样可以避免悬挂边
1 个赞
因为 1.0 和 3.x 的编码区别比较大,可以参考文章
江一旺
9
不太理解 ”扫描vertex就会很大可能大量无效的磁盘数据加载“, 如果只 scan 1w 个 vertex 是不会读边上的数据的。
一下为1.0的ScanVertexProcessor.cpp的源码,通过源码是可以看到扫描的时候的确不区分点和边
std::string prefix = NebulaKeyUtils::prefix(partId_);
std::unique_ptr<kvstore::KVIterator> iter;
//这里并没有类型作为前缀的一部分
auto kvRet = doRangeWithPrefix(spaceId_, partId_, start, prefix, &iter);
for (; iter->valid() && rowCount < rowLimit && blockSize < FLAGS_max_scan_block_size;
iter->next()) {
auto key = iter->key();
//这里还加上了是否为顶点的判断
if (!NebulaKeyUtils::isVertex(key)) {
continue;
}
刚看了下3.6的代码,比1.0的难懂了很多
,有点看不懂
江一旺
10
3.6的找到了,的确扫描的时候已经区分点或者边了,那就是在低版本的nebula存在我说的那个问题了
std::string prefix = NebulaKeyUtils::tagPrefix(partId);
auto kvRet = context_->env()->kvstore_->rangeWithPrefix(
context_->planContext_->spaceId_, partId, start, prefix, &iter, enableReadFollower_);
1 个赞