如何提升 meta 性能?提高 TTL 删除速率?主备集群怎么做…Happy Office Hour 第一期会议纪要告诉你

第一期 Happy Office Hour 线上交流会因为时间的关系,有 3 家公司参与了本次交流。因为企业信息敏感的缘故,本文经过脱敏后,现将 NebulaGraph 使用技能分享给大家一起研究探讨下,同这 3 家企业交流过程中遇到的产品需求,可移步至本文文末查看。如果你觉得这个需求同你的业务需求相似,不妨给它投上一票,人工提高它的开发优先级。

本文目录

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

这里用 H1、H2、H3 作为代号指代这三家公司,Muyi 指代 NebulaGraph 产品总监方扬,Yee 指代 NebulaGraph 资深研发伊兴路、Critical 为资深研发王玉珏,亚军为 NebulaGraph 实施陈亚军缩写;

多场景、百亿数据的企业 H1

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:目前,NebulaGraph 开源版本有过实践的集群规模最大是多少节点,及其是什么配置、什么数据量级,meta、graph、storage 如何部署?

Muyi:据我所知,NebulaGraph 客户最大的集群规模是百台 graphd + storaged。这个客户采用的部署方式是容器化部署,但其实理论上如果你的集群部署使用物理机和虚拟机的话,性能可能会更佳。

flink-connector 导入的性能

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 资源?

Yee:在我们进行数据导入的时候,一般都是通过连接池配置好特定的线程数去和 graphd 进行连接,而线程内部会进行负载均衡(load balance),不需要每个 graphd 都创建 800 个连接。像 nebula-importer 的话,是支持配置一个 graphd 的列表,内部会轮询,发送数据。正如亚军所说的,导入性能要高的话,并不要求客户端并发设置很高,才能提高吞吐和速度。 Muyi:建议可以增加 graphd 的数量,再调小 batchSize,因为你们的 value 比较大。此外,可以建议你换 nebula-importer 进行下压测,因为它是本地读 csv 文件,再导入到 NebulaGraph 中。在 nebula-importer 侧做些配置,可能会有个不错的性能。瓶颈在哪块,就增加对应的配置。

亚军:之前我单副本、3 台物理机,用 LDBC SF100 的数据集、万兆网络,单副本的情况下最高的速率是 600 w/s。建议你可以用标准的数据集测试下你们机器的导入数据,看下情况。关于 LDBC-SNB 数据集,具体请参考以下文档:

Partition 的规划

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

Yee:考虑到阔缩容的需求,一般建议是磁盘数的 10 倍以上。

H1:我们的实际测试中,如果 24 台机器、100 个 Space 的情况下,Partition 设定 1,000,整个集群容易 Offline、各种 SHOW 语句会明显地卡顿,以及 Storage Offline 的问题遇到的概率也会增大。

Yee:Partition 不一定是越大越好,里面会涉及到很多的资源分配。你设置 Partition 多了的话,是会比较占资源,里面的线程间也会有并发的竞争。

Critical:你举的这个例子,Space 数量已经不小了,这个时候仍然设置为 1,000 个 Partition,是有可能出现资源不够的问题的。

亚军:我们之前有个客户,数据量也挺大的,机器比较多,Partition 配置在 810, 集群运行了很久非常稳定。

H1:此外,我们的 Heartbeat 的值设置在了 30s,两个心跳时间之后响应服务,是不是有点慢?

亚军:设置成默认值(10s)就够。

一般的性能瓶颈

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

亚军:大部分我们这边碰到的瓶颈是出现在 Compaction,比较少见到你们这种瓶颈在 graph 的情况。所以之前你说 graph 被打爆了,第一反应就是 800 的并发太大了,其实用不到那么大的并发。一般来说,实施这边常遇到的是,用户的 VID 长度设置得太大、Compaction Job 没调,Partition 数量设置的太小等这些都是比较影响性能的。

TTL 的删除速率

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

Critical:你可以看下这个 pr #5622,修改 min_level_for_custom_filter 这个参数。这个参数的值越小,删除 TTL 的速度会越快。如果还达不到预期,可以不断手动进行 Compaction。

数据血缘的企业 H2

H2:科技公司,目前业务信息:

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

模糊检索

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

Muyi:这个做法不是特别的通用,一般来说的话,可以基于多个 tag 属性创建索引,然后全文索引查询的时候对这些属性进行统一查询。比如说,tag1 有 name 属性,tag2、tag3 也是,你可以创建这 3 个 tag name 属性的全文索引,做相关查询的时候对这 3 个全文索引进行检索,再最后做个 union 合并下。

亚军:其实 H2 目前的用法,在这个业务场景下更合适,相当于利用多 tag 的特性,为所有其他的 tag 创建一个公共的 tag 来存放用来搜索的属性,基于这个属性创建一个全文索引,根据这个全文索引再找到对应 VID 匹配到对应的点。新的 tag 相当于就是为了全文索引专用。

H2:我们这边还有一个额外的关联性需求,就是关联性高的话,结果排序更靠前。

Muyi:在 v3.6.0 开始,ES 那边会有一个得分项,这个得分相当于是原始 ID 的分值。当你做一度查询时,将其乘以一个系数;在做二度查询时,再将其乘以一个系数,这样全文的关联性系数就能全部获得,最后你再做一个整体的得分排序,就行了。

H2:所以这个排序主要是在 ES 端实现的?

Muyi:不是的,ES 其实只会返回你一次全文索引的分数,你可以基于这个分数在做 MATCH 之类的查询时,做一度、二度的扩展,再去计算每个邻居的分数。

Yee:其实在这里有一个歧义点时,这个关联性是文本的关联度呢?还是点边关系的关联性。如果是文本的关联性的话,是在 ES 实现的;而后者则是在图数据库中实现的。NebulaGraph 全文索引这块的话,返回查询结果时,不只是返回结果,还会把 ES 当中的打分(即:score)返回,可以基于它的打分来判断关联性。

H2:这个打分的功能时在什么版本上支持的?

Yee:最新的 v3.6.0 中对全文索引进行了重构,支持了 score 功能。

权限控制

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

Muyi:目前点、边颗粒度的权限,是在企业版上支持的。如果是你们这边的话,可以在应用层进行权限管控。

Yee:内核这边权限的配置是存在元数据中,每次查询时元数据会查阅数据中该用户对应的 tag、edge 的操作是否有访问权限,比如读权限还是写权限。在查询层(语言层)就能进行一些权限验证,而这些验证遵循 RBAC 的访问控制,看给哪些用户配置了哪些表的读权限和写权限。

H2:看样子不是基于应用层的权限来做的权限管控?

Yee:应用层业务的话,一般来说都有一套账号体系对应到图数据库这边,应用那边有一个 user,对应到图数据库这边也会有一个 user,至于他能访问哪些点边,就要参考应用层那边的原始设定。

主备集群

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

Muyi:这种主备场景的话,目前是在企业版上做的支持。社区有一篇文章[《架构设计——基于 raft-listener 实现的 nebula 实时同步的高效双集群主备环境》(架构设计——基于 raft-listener 实现的 nebula 实时同步的高效双集群主备环境) 这里提供了一个实现思路,需要进行一些定制化开发工作。不过,回到你这个例子,其实你 CPU 被打爆的话,应该不是主备集群能搞定的事情,而是你本身的业务压力有点大了,你可以走分布式路线,扩充你的 storage 和 graph。

特征存储的企业 H3

H3:科技公司,目前业务信息:

  • 状态:业务开发适配中
  • 数据量: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 测试、覆盖率测试还有调试等等。

meta 性能

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

Yee:meta 这块的话,目前会有个 slow query 写入的问题,meta 会记录一些比较慢的 query,标记成 slow query,这块有可能是个性能瓶颈。

H3:目前来说 meta 都是单 part 的,有考虑说做成多 part 么?

Yee:因为元数据不同于真实的数据,不需要很高的并发、数据量大了要做切片,其实元数据没有那么大的需求。

H3:多 part 的话就是可以分散请求压力。

Yee:其实这个和 meta 的设计有关系,合理的话 meta 是不应该有很高的负载,不管出于什么原因,meta 的负载不应该是很高的。

Critical:尽量减少 Session 的相关操作,绝大多数 meta 的压力其实是来自与 Session。另外更新 schema 的频率不应该太高。

内存控制

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 端口。

Critical:新的版本好像可以支持换 ip/port。

Happy Office Hour

Happy Office Hour 活动暂定为每月一期,每期来自 NebulaGraph 的内核开发同学以及产品同学,将在活动约定的 2 个小时内,通过线上腾讯会议的形式 ,和大家交流遇到的使用问题。

每期 NebulaGraph 会从报名表中,选择 2、3 名报名用户加入私密的腾讯会议。在该次线上会议,同来自这 2、3 家公司的社区小伙伴交流使用心得。

想要和 NebulaGraph 核心研发来一场线上的面对面交流么,记得报名Happy Office Hour:https://wj.qq.com/s2/13521700/1698/ (同二维码)

image

Tips:下一场交流会将在 01.26 下午 15:00 正式开启~~


对图数据库 NebulaGraph 感兴趣?欢迎前往 GitHub :sparkles: 查看源码:https://github.com/vesoft-inc/nebula;

想要一起提高文档的可读性么?一起来给『文档 nGQL 示例添加注释』吧~请瞄准那条让人费解的 nGQL 语句,留下你的讲解 (///▽///)

点击下图了解活动详情 :face_holding_back_tears:

image

3 个赞