
多场景、百亿数据的企业 H1 合理的部署方式 flink-connector 导入的性能 Partition 的规划 一般的性能瓶颈 TTL 的删除速率 数据血缘的企业 H2 模糊检索 权限控制 主备集群 特征存储的企业 H3 存储的数据类型性能 存储取数 问题定位和调试 meta 性能 内存控制 端口切换


H1:科技公司,目前业务信息:
状态:已在上线上使用 NebulaGraph
数据量:200 TB
单机器标准配置:CPU 104c,内存 700 GB,磁盘 4 块 2.9 TB 的 SSD
业务场景:
传统的知识图谱,主要解决风控问题。系统存储用户的相关数据,以离线数据为主。数据量在百亿级别,为 T+1 型数据;
特征工厂,采用图数据库来存储特征。由于用户的特征较大,单个点的属性值可长达 200k。此外,这块的写并发较高,每秒 15w 左右点数据写入;
广告推荐,主要为图谱召回,进行 u2i 和 u2u 相关数据检索;
集群:10+ 套集群
版本:v3.2.0 和 v3.4.0;


“ H1:目前,我们的配置就是上面罗列的那样:裸金属,104c,700 GB,单机 4 块 SSD 共计 10 TB 的配置。而我们进行资源评估时,如果是 3 副本,单磁盘(存储)方面,按点和边来算的话,在特征工厂业务场景下,数据的窗口期是 2 小时,在此期间会有 100 TB、十几亿的数据流动,需要 70~80 台上面所罗列的单机配置。
我们之前用 24 台标准配置,partition 设 100,发现数据导入的 IO 和磁盘容量并没有达到瓶颈,但 CPU 已经达到上限,graphd 内存会打满。而导入速率这块,24 台单副本、50k 一条数据的话,导入速度是 11w/s,远低于我们的目标 78w/s。在测试过程中,我们发现 24 台机器的情况下,单台机器的 IO 能达到 200+ MB/s;而缩小集群为 3 台的话,最高速度能到 300 MB/s。
基于测试的这种情况,我们根据业务规模,估算了下大概要 70~80 台机器。这是按照存储来估算的,如果按照吞吐来算的话,机器台数会更多。
所以,在这种情况下,我们如何优化写入性能?提高吞吐呢?”
亚军:首先看下导入速率的瓶颈在哪里。
“ H1:瓶颈在 graph 这边,我们用上了所有的 graph 服务,并发起来之后,graph 这边的 CPU 消耗能到 99%。在一些场景下,像 800 个 Flink task 的情况下,将并发从 800 提升到 2,000,基本额上没有导入的吞吐增长。800 并发的导入性能是 11 w/s,拉到 2,000 并发,还是 11 w/s,这时候,CPU 消耗非常高。三台机器规模的情况下,通过火焰图观测到 graph 解析 nGQL 耗时中 nebula:GraphScanner:~GraphScanner/yyFlexer:yy_get_previous_state 消耗了 75% 的时间,剩下的 25% 花在了 NET IO 那边。这是使用 Flink 3.3 的情况下,如果切换到 Flink 3.5 的话,这个函数消耗能达到 90%,这时候 NET IO 在 10% 左右。”
亚军:根据过往的实施经验,并发并非越大导入性能越好。在你这种情况下,800 并发可能设定稍大。你这边单条 INSERT 语句的长度是多长?batch size 设置多大?
“ H1:batch size 设定是 24w,数据是 100 ms 写入一次。我们看过统计,在单条记录 50k 的情况下,每次可能只能写入几十条。这边数据导入的模式是 kv 形式的,vid 就是 key,value 的话是一个长字符串。value 中存储的是特征,因为没有做属性的划分,所以一股脑地将特征存储到字符串,交由上层来处理。”
Yee:这个 case 比较特殊,相当于一个字段承载所有的属性。从上面的描述来讲,是 Parser 解析的问题,Flex/Bison 解析初始的语句的字符串比较耗时。
“ H1:目前,我们这边能统计到的 IO,如果是 24 台机器的集群,大概是 200 MB/s;而论坛之前有用户分享过导入性能,吞吐差不多是 100 MB/s。这里想问下,一块 SSD 是 100 MB 的话,是不是说明 Flink 和 NebulaGraph 结合导数的话,差不多极限就是这个速度?”
Muyi:如果说这个数据没法细分做切分,所有数据都加载过来的话,应该只能通过添加 graphd 的机器来提速了。
“ H1:如果是添加 graphd 的话,对应的连接数会变高,这时候会不会导致大量的 NET IO 消耗,最后占大量的 CPU 资源?”
LDBC-SNB Specification:https://ldbcouncil.org/ldbc_snb_docs/ldbc-snb-specification.pdf LDBC-SNB Docs:https://github.com/ldbc/ldbc_snb_docs LDBC-SNB 测试数据集生产工具:https://github.com/ldbc/ldbc_snb_datagen_spark

“ H1:文档上建议说集群的总磁盘 x 20 这个数量级,这个是比较通用的么?”

“ H1:一般来说,性能瓶颈会出现在哪块呢?是 graph 还是 CPU 内存,这块有没有什么分享的经验?”

“ H1:前面我们说过特性场景下,我们的数据窗口期是 2 个小时,没有办法在一边写入 400 MB/s 数据落盘的情况下,打开 Compaction,又借助 TTL 删掉 400 MB/s 的数据呢?目前,我们观察到的现状是虽然 TTL 也在删数据,但是删除的速率是远低于导入的数据的。”


状态:已在上线上使用 NebulaGraph
数据量:10 万
单机器标准配置:8c、16 GB
业务场景:
数据血缘和数据分析,在提供给上层业务同学进行找数据服务的过程中,业务同学会配置一些数据源、指标表等内容,但这些指标和数据源之间或存在调用、上下游关系,目前是借助图数据库来实现这块的数据治理。
版本:v3.6.0

“ H2:我们这块有功能模块是需要用到模糊检索的,用户在这个模块配置他所需的关键字信息之后,可以匹配出相关的一些列表、视图等等字段信息。目前,我们使用 NebulaGraph 的全文索引的 3.6.0 版本。我们全文索引的构建方式是单独创建一个 Node(tag),里面存储其他 Node(tag) 的关键信息,基于这个单独的 Node(tag) 再创建全文索引。”

“ H2:因为这块是数据血缘场景,涉及到一些表、数据源的权限管控。不知道 NebulaGraph 这块是如何数据管控?”

“ H2:这里再问一个数据迁移的问题。之前大促时,一个 8c、16g 集群突然 CPU 被打满了,临时紧急申请了一个 32c、64g 的三台机器去替换。想问下 NebulaGraph 这边有没有类似的主从机制,这样我们可以申请两套机器,一个线上用,另一个做备用。遇到故障的话,可以快速做一个切换。”


状态:业务开发适配中
数据量:100 亿
集群配置:9台(32c、64 GB 内存、4T 硬盘)
业务场景:
特征数据存储:从用户的行为历史数据提取出特性,再存储到 NebulaGraph 中做下一步的数据处理;
动态实时计算:对 KV 抽取的性能要求高;

“ H3:因为特征场景,可能我们需要处理一些 Embedding 和 Binary 的数据类型。虽然 NebulaGraph 这两个类型不支持,我们内部进行了数据结构的扩展,将其序列化成一个二进制,存储到 NebulaGraph 中。目前来看,这种实现方式存成二进制和 String 性能上来看,差不多。因为像 SQL 语句存成 Binary 的话,要通过 Base64 进行转化,和纯 String 相比,除了底层上会有 25% 的数据膨胀的减少之外,其他的效果并不是很明显。同样的 Embedding 类型因为 Java 客户端不是很好适配,所以在 Java 客户端进行数据抽取时,要进行转换,将 Embedding 转化成 List 类型,这个性能开销也比较大。这块性能优化,有什么建议么?个人觉得,Embedding 这块的耗时是在序列化和反序列化。”
Yee:之前 NebulaGraph 规划过 List 数据类型的支持,尤其是同构的数据类型,像是 List Double。不过这种结构可能对解决一维数据比较有用,而你的数据如果是二维的话,便是一个矩阵,并且可能不是一个规范矩阵:第一个 List 里面是三维数据、第二个 List 是多维数据...
“ H3:这边的话数据的话都是规范矩阵,不只是支持一维、二维,还要支持高维数据。但是它还是非常规范的矩阵,比如说它是二维的话,第二维就会都是三维或者是四维,不会出现第二维度不一致的情况。数据类型也是规整的,都是整型或者都是浮点型。数据结构这块我们已经内部实现了,但是最大的问题是查询性能不是很好。”
Yee:查询的话,主要是基于什么场景下的查询?
“ H3:主要是 GO 语句,从一个点出发,去查询另外一个点,找到存在这个点上面的 Embedding 属性,而这个 Embedding 属性大概在 200 位左右,Value 比较大。”
Yee:这边查询性能不好的话,其实要具体看下哪块有问题。是因为数据量太多,它要扫磁盘,导致时延比较高,还是因为它往外拓展找关联关系的时候,查询耗时比较高。
“ H3:耗时的话,其实有一个原因是我们的客户端对新的数据类型没有做适配,取数的时候需要做一层转译。Embedding 数据从 Storage 存储取出来之后,到 graph 那边时,需要反序列化成我自定义的 Embedding 结构之后,Java 客户端因为没有适配,需要转成 List 这种我们现在支持的数据类型,上层才能读到数据。我觉得这个是比较大的瓶颈。”
Yee:这个没关系的,因为数据返回的时候,Server 端不是有一个 latency 的统计么?Server 端的统计是不包含网络传输和客户端的反序列化耗时的。
“ H3:所以我理解,要想性能好的话,客户端这边的话就不做类型转换,不访问它的数据结构。其实我之前有想过直接支持 value 类型,直接将序列化的内容存储到 Storage 中,然后取数的话也是直接把序列化的内容传给 graph,graph 再把序列化的内容填充到 RPC 的 Protocol 协议中。”
Yee:value 的话,它的 Overhead 会比较高。空间占用也会比较大,像一个 bool 类型,存 1 个 value 的话,它本身字节就 24 个字节,这块的空间就会很浪费。如果是 List 类型的话,这块的空间占用问题就不会那么显著。但是你如果存储一些基础类型的话,就会比较明显。不过一旦存储那边读出来数据,差异就不大。因为内核现在查询和存储的传输,也是用的 value。

“ H3:目前我们这边有一个需求:单独弄一个接口用来做 KV 接口。这个接口主要是屏蔽掉查询这边的一些执行计划的流程,直接 Fetch 取数不通过执行计划,调用 Storage 的接口去取数。这样性能会提升不少。”
Yee:其实 NebulaGraph 有 storage client,它的作用和你说的这个是类似的。你可以直接调用 storage 客户端来满足你这个业务需求。本质上就是压 RocksDB 的性能。

“ H3:这边日常会遇到一个定位的问题,不知道 NebulaGraph 这边有什么调试的方法。我这边如果要定位一些问题的话,因为线上的 Space 比较多可能是几个百,每个 Space 又有上百个 Partition,这时候如果打开日志的话,日志级别打到一级或者三级,日志刷刷刷地写得飞快,要在那么多信息中找到想要的日志信息,是挺难的。不知道这边有没有一个功能,针对某个 Partition 打开对应的日志,方便做后续的调试或者定位。”
Yee:日志这块我们是用的 glog 实现,glog 有一个 vmodule,可以基于它过滤一些日志。
Muyi:如果你的实时性不高的话,可以将日志读取到 ES 中,进行读取和分析。
“ H3:其实我还想问,像 NebulaGraph 日常定位的话,是只通过日志,还是有什么其他手段在定位问题上会比较快速一点?”
Yee:如果是线上定位的话,依赖日志会比较多点。如果说是研发定位的话,手段就相对多了。你可以做断言、Chaos 测试、覆盖率测试还有调试等等。

“ H3:我这边会遇到一些 meta 相关的问题,以后我们的 meta 会有高频的发心跳操作,因为我们的 Space 和 Partition 数量比较多(上百个 Space、每个 Space 上百的 Partition),如果 meta 发心跳去更新 schema 的话,会发送很多请求,然后这个请求一次可能就好几百条,这时如果是单节点的 meta 的话,它可能会有性能压力,不知道有什么好的建议没?”

“ H3:v3.4 版本好像有上了一些内存控制的功能,我们这边使用的话,也会到了一些内存上涨的问题。不知道 NebulaGraph 这边有什么内存调参和 RocksDB 的优化建议?”
Yee:内存这块涉及到两块,一个是 RocksDB 存储这块的,另外一个是查询层面的内存使用。大概是 3.4 之后,主要做了两点优化:第一点是减少内存使用,像相同的点边 value 它要去共享这个点边,这样减少内存使用;第二点就是控制内存使用,就是为了保证进程还能对外服务,可能会优先杀掉一些 query,会牺牲部分的 query 来换取服务的稳定。主要是 memory tracker 和 kill slow query 之类的功能,像结束慢查询的话,一般优先杀掉最占内存的 query。后面有规划,让用户可配置每个 query 可以分配多大的内存,如果超过了配置就继续数据落盘。
“ H3:我们这边主要是 Storage 的内存占用,应该是写缓存占用比较大。多 Space 同时写的时候,写缓存就比较大。我现在的写缓存主要是 RocksDB 那边的 read-buffer,它的写缓存好像是 raft-buffer x 块数就是 Space 的大小。”
Yee:nebula-storage 本身有一些 buffer,它会缓存一些部分来不及处理的数据和日志,还有 RocksDB 的数据缓存这部分,可以关注下。
Critical:raft buffer 的大小由这个控制 max_log_buffer_size。

“ H3:我们整个集群进行迁移时,我看到 NebulaGraph 这边有个 replace 接口可以换 IP,但是有时候我也想更换端口。如果在配置里换成新的端口的话,meta 那边不能识别到新的 Storage 端口。”
Muyi:Storage 的话,可以 Add Hosts 新的机器端口之后,进行 data balance remove,再 drop 掉对应的老的 Storage 端口。
想要和 NebulaGraph 核心研发来一场线上的面对面交流么,记得报名Happy Office Hour:https://wj.qq.com/s2/13521700/1698/ (同二维码)


对图数据库 NebulaGraph 感兴趣?欢迎前往 GitHub ✨ 查看源码:https://github.com/vesoft-inc/nebula;
想要一起提高文档的可读性么?一起来给『文档 nGQL 示例添加注释』吧~请瞄准那条让人费解的 nGQL 语句,留下你的讲解 (///▽///)






