知识图谱数据库是 NoSQL 数据库中增速最快的一个分支,它在大数据和人工智能领域的地位逐渐凸显。但是目前主流的图数据库产品大都属于海外产品,且售价极其高昂,为了解各大主流图数据库的读写性能指标,特将国产的新兴图数据库 AbutionGraph(AbutionGDB)与 Neo4j,JanusGraph,TigerGraph 等占据着市场 95%份额的主流图数据库做了读写性能对比测试。特别说明:AbutionGDB 是唯一面向 OLAP(数据分析决策)场景的图数据仓库,而其它对比者是面向 OLTP(数据增删改查)的图数据库,不过这并不影响读写性能的测试。
图 1 数据示例
图中的数据时间戳在测试中进行了调整,进入到系统中的时间戳没有采用如图所示的非严格的 RFC3339 格式,而是使用与 1970-01-0100:00:00(UTC)时间的差(精确到毫秒)的时间戳表示方式(8bytes 的长整型),进入到系统中的金额数据也没有采用如图所示的 Double 标准双精度浮点数类型表示,而是采用 Bigdicemal 非基本类型来存储交易金额,因为交易金额很大时,与历史记录聚合很可能发生损失精度的情况,由于系统错误导致的财产损失这是绝对不允许的。为便于测试,实际数据均由计算机随机生成,实体数限制为 10w 个并按照上述格式和数据库语法格式写入各数据库。
因为每款图数据库使用的语言和方式都不一样,本次主要测评过程以图谱建模的最终结构为标准,使用的 KnowledgeGraph 建模如下:
对于任何一个测试,每个操作过程重复运行 5 次,且最终的统计结果是 5 次测试结果的算数平均值。
每次测试进行之前均重启数据库服务,避免缓存对于之前执行获得的结果产生影响。
本文中所说的延迟(latency)是指全部完成该操作所耗费的时间,例如针对多条记录的查询操作,其时间延迟是指完全将结果获取到客户端后,相对于请求发出时间之间的间隔。吞吐量(throughput)是单位时间内完整完成该操作的数量。
所有数据库的对比测试在同一台 8H16G 的阿里云服务器上进行,该服务器的详细配置如下:
架构: x86_64
CPU: 8
CPU 运行模式: 32-bit, 64-bit
Size: 16 GB
每个核的线程数: 2
每个座的核数: 4
座: 1
厂商 ID: GenuineIntel
CPU 系列: 6
型号: 85
型号名称: Intel(R)Xeon(R)Platinum8269CYCPU@2.50GHz
CPUMHz: 2499.998
BogoMIPS: 4999.99
超管理器厂商: KVM
虚拟化类型: 完全
LSB Version: core-4.1-amd64:core-4.1-noarch
Description: CentOS Linux release 8.2.2004(Core)
本测试将 AbutionGDB 与 Neo4j,JanusGraph,TigerGraph 等图数据库进行了对比,所测试的 Neo4j 版本为 3.5.24;JanusGraph 的版本为 0.5.2(使用 Hbase 后端存储);TigerGraph 的版本为 3.0.0,而 AbutionGDB 的测试版本为 1.3.0,截止至 2021 年,皆为 jdk-1.8 支持下的最新版本。
在测试时,AbutionGDB、Janasgraph、Neo4j 均使用 JavaAPI 并结合各自的查询语言进行数据操作,由于 TigerGraph 只提供了 HTTP 接口,在测试中我们采用 Java 语言的 Apache HTTP lib 库来编写测试程序。
AbutionGDB 提供更优异的异步接口,支持使用 Flink 作为大规模实时数据源写入数据,但 Janasgraph、Neo4j、TigerGraph 均不支持。只有 AbutionGDB 和 TigerGraph 可以使用 Kafka 作为实时数据源写入数据,为了测试一致性及公平性,仅使用各自推荐的同步接口方式进行读写测试。
在本次测试中,AbutionGDB、Janasgraph 及 TigerGraph 都采用默认配置,Neo4j 则只修改 dbms.memory.heap.initial_size 和 dbms.memory.heap.max_size,由默认的 512m 改到 5g,以最大程度发挥 Neo4j 性能。
为避免网络延迟的影响,应用与数据库均在同一台服务器上运行。为更进一步控制变量,本测试确保在对每个数据库进行测试时只有该数据库运行,因此数据库所能占用的 CPU 资源最高可达 100%。
本测试测试了各个数据库在不同客户端连接数同时写入和读取不同批量数据的表现,且每种情况均进行至少 5 次测试,最终结果为 5 次测试结果的平均值。同时为了避免测试与测试之间的干扰,每次测试结束后,都会删除数据,重启数据库服务并且等待 5 分钟左右。
数据库的一个写入请求可以包含一条或多条记录,性能结果取每个客户端每秒内同时写入数据量的总和,并取多次平均值。总而言之,一次请求里包含的记录条数越多,写入性能就会相应提升。
同时,一个数据库可以支持多个客户端链接,链接数增加,其系统总的插入通吐量会相应增加。因此测试中,对于每一个数据库,都会测试一个客户端和多个客户端连接的情况。
在 AbutionGDB 中,可以定义任意多维度的指标列,其中动态聚合的存储模型是其特有的,其中预计算模型可以大大提高查询性能,而静态历史数据的存储模型是与 Janasgraph、Neo4j、TigerGraph 一致的,为了凸显写入事件的速率和查询性能的公平性,我们定义每一条完整的原始交易数据入库完成才作为写入一条数据,即一条事件数, 而不是以每个实体或者每条关系作为一条数据。
此测试中,基于 AbutionGDB 强大的吞吐量,我们专门把写入事件数的区间拉大,按照每次请求包含 1,100,1000,10000,100000,200000 条事件记录各进行了测试,同时也测试了不同客户端连接数的数据。具体结果如下:
批次插入事件数 | 1client | 2client | 3client | 4client | 5client | 6client | 7client |
---|---|---|---|---|---|---|---|
1 | 1339 | 2628 | 3904 | 5177 | 6516 | 7843 | 9243 |
100 | 24599 | 50099 | 71167 | 87467 | 99567 | 112667 | 136267 |
1000 | 44343 | 84931 | 119931 | 145931 | 166907 | 195273 | 235861 |
10000 | 58878 | 95295 | 130416 | 160418 | 190975 | 199025 | 236070 |
100000 | 99040 | 129773 | 172025 | 196447 | 232315 | 267567 | 251729 |
200000 | 187402 | 229706 | 207413 | 233558 | 263547 | 264271 | 267015 |
因本测试将 3 条图数据统一归为 1 条事件数统计(每条事件包含 2 个实体 1 条关系),按照数据库写入性能标准,以上测试结果应该均乘以 3 即为真实写入速率。具体结果如下:
批次插入事件数 | 1client | 2client | 3client | 4client | 5client | 6client | 7client |
---|---|---|---|---|---|---|---|
1 | 4017 | 7884 | 11712 | 16713 | 19548 | 23529 | 36729 |
100 | 73797 | 150297 | 213501 | 262401 | 298728 | 338001 | 408801 |
1000 | 133029 | 254793 | 359793 | 437793 | 500721 | 585819 | 707583 |
10000 | 176634 | 285855 | 391248 | 481254 | 572925 | 597075 | 708210 |
100000 | 297120 | 389319 | 516075 | 589341 | 696945 | 802701 | 755187 |
200000 | 562206 | 689118 | 622239 | 700674 | 790641 | 792813 | 801045 |
上述结果清楚表明,AbutionGDB 的写入速度随客户端连接数的增加近乎线性增加,而且随单请求中记录数目的增加而增加。在七个客户端下,一个请求,写入 1 条数据时,AbutionGDB 就有超过 10,000 记录/秒(30,000 条图数据/秒)的写入速度。在一个写入请求 200,000 条记录时,在 7 个客户端同时写入时的情况下,AbutionGDB 有高达 267,015 记录/秒(801,045 条图数据/秒)的写入速度。同时,从上图中可以看出,这个速度并非 AbutionGDB 写入速度的上限,该速度还会随客户端数目的增加而增加。
在测试中,我们尝试了两种方式来更全面的测试 Neo4j 写入性能,第一种是传统的 JDBC 方式,通过 DriverManager 接收 SQL,同时禁用了 AutoCommit,采用批量手动提交 commit 的方式,Neo4j 本身没有此项功能支持,需要额外下载 neo4j-jdbc-driver 和 neo4j-jdbc-bolt 扩展包支持;第二种是使用的是官方推荐的 bolt Driver 连接方式,此方是没有 commit 方法,Neo4j 自动提交数据变更,所以测试只能通过客户端的连接数观察写入性能,为此,我们做了 5 组测试来分别观察客户端的连接数对于写入性能的影响。由于 Neo4j 不支持 BigDecimal 数据类型,因而交易时间将采用 Double 来存储。
通过测试发现,两种方式对于写入速率没有实质性差距,Neo4j 的批量提交写入并不能有效提升其写入效率,因此仅仅记录了 Neo4j 连续写入的多个客户端同时写入来达到批量写入数据的效果。结果如下:
事件数 | 1client | 2client | 3client | 4client | 5client |
---|---|---|---|---|---|
test1 | 72 | 68 | 63 | 56 | 59 |
test2 | 70 | 70 | 64 | 54 | 61 |
test3 | 73 | 68 | 62 | 55 | 56 |
test4 | 73 | 68 | 62 | 55 | 53 |
test5 | 72 | 68 | 66 | 56 | 57 |
从上述结果表可以看出,Neo4j 仅适用于单客户端单条记录的写入,多组对照实验结果几乎相同,证明其写入性能并不会由于客户端链接数量的增加或单请求中记录条数的增加而显著改变。
在测试中发现,JanusGraph 的写入吞吐量与客户端连接数没有太大关系,而与批处理时单请求中的记录数相关。批请求中记录数目越大,其吞吐量也越大,但是当数据量达到 1w 左右的时候就出现了严重的写入抖动,速率在 3000-1w/s 间波动。由于客户端连接数对 JanusGraph 的吞吐量影响较小,本测试只给出 JanusGraph 在单客户端和双客户端连接情况下的测试结果,且分别对每个客户端按照每次请求包含 1,100,300,500,1000,2000,10000,20000 条事件记录作出测试。测试结果计算为每秒写入的数据总量,多秒完成的写入计算结果为平均值。测试结果如下:
事件数 | 1 | 100 | 300 | 500 | 1000 | 2000 | 10000 | 20000 |
---|---|---|---|---|---|---|---|---|
1client | 316 | 3700 | 5400 | 8300 | 8200 | 8000 | 6666 | 6999 |
2client | 4140 | 5070 | 7220 | 5450 | 5950 | 5800 | 7250 | 6333 |
上述结果表明,JanusGraph 的写入速度随单请求中的事件数目的增加而增加,但最终增长速度趋缓,并在每次提交事件数在 1000 条左右到达写入最佳性能,同时也到达了 JanusGraph 的写入瓶颈,再随着每批次写入数据的不断增加,写入性能不增反降,且最终写入速度维持在 7000 事件/秒上下。
本次我们测试的是 TigerGraph,由于 TigerGraph 官方只提供基于 HTTP 的 REST 使用接口,此方式写入较慢,无法批量写入,所以我们使用开源的 jdbc 方式,且修补了一些版本匹配问题,此方式其实是 HTTP 接口的一种封装。Jdbc 提供了 3 种写入方法,提交 job 的方式是离线导入,第 2 种方法 PreparedStatement 的写入语法对 Double 数据类型参数支持不友好,最终我们选择使用 Statement 方法写入,并以默认的覆盖历史数据的形式写入新数据,对不同对照组的 Batch 进行批量提交写入,Statement 的方式可以以批量提交的方式进行写入,与 AbutionGDB,Neo4j,JanusGraph 测试方式一致。
在测试过程中发现,TigerGraph 的写入性能与客户端连接数没有太大关系,与批处理时单请求中的记录数也没有太大关系,反而会略微拉低其它客户端的写入能力,所以本测试只记录了 1-5 个客户端的同时写性能,测试结果如下:
事件数 | 1client | 2client | 3client | 4client | 5client |
---|---|---|---|---|---|
1 | 143 | 267 | 338 | 469 | 450 |
100 | 250 | 400 | 441 | 580 | 625 |
1000 | 350 | 544 | 588 | 780 | 971 |
2000 | 351 | 510 | 702 | 888 | 976 |
10000 | 370 | 606 | 714 | 848 | 983 |
上述数据表明,基于 HTTP 接口的数据写入方式并没有很高效,随着客户端连接数和实时写入数据量的增加,写入性能成弱线性增长。
鉴于以上 TigerGraph 的测试结果不是很高效,我们专门对 TigerGraph 测试了离线写入能力,即 load job 数据写入方式,这是官方文档推荐的做法。为了记录离线写入性能,我们使用与 AbutionGDB,Neo4j,JanusGraph 相同的数据生成程序随机生成一批数据到文件,并将每一条事件记录拆分为节点数据和关系数据两个文件分别等待写入,然后预先分别对每批次写入数据的 5 个对照组预先创建 LOADING JOB(不计入写入耗时中),最后在 gsql 脚本中执行批量导入 JOB 并记录耗时,最终统计得出每秒写入性能。经测试一两条、几百条等少批量数据导入对性能表现不明显,为了节省测试时间,避免生成离线数据的繁琐过程,我们将从 100 条每批写入开始记录结果。
在测试过程中发现,TigerGraph 的写入性能与客户端连接数没有关系,反而会均分掉其它客户端的写入能力,所以本测试只对 TigerGraph 在 1 和 2 客户端连接的情况下的性能进行了测试和分析,测试结果如下:
事件数 | 100 | 1000 | 10000 | 20000 | 60000 | 100000 | 200000 | 500000 |
---|---|---|---|---|---|---|---|---|
1client | 17 | 167 | 1751 | 3076 | 10000 | 14285 | 28571 | 76923 |
2client | 10 | 91 | 952 | 2371 | 6315 | 9524 | 21052 | 47619 |
上述结果表明,TigerGraph 的离线写入速度随单请求中记录数目的增加而增加,入库速率与批次入库数量成正比增长,数据写入及与节点个数成弱线性增长,也与实时写入测试的结果增长趋势成线性关系。
注意:本测试不与 AbutionGDB,Neo4j,JanusGraph,TigerGraph 已测试过的结果进行比较,因为实时写入涉及的数据库 cache 和 flush 的性能是性能测试和稳定性测试的重要指标之一,与离线数据测试机理不同,TigerGraph 离线导入使用 MapReduce 并行写入,这与 AbutionGDB 和 JanusGraph 中的离线导入方式基本相同,故此不再展开分别测试。
TigerGraph 实时写二次测试
由于以上 TigerGraph 的测试结果与官方报道的性能差距太大,所以我们决定增加测试 Kafka 的写入接口,为了尽量保证公平性,我们还是使用与 AbutionGDB,Neo4j,JanusGraph 相同的数据生成程序随机生成一批数据到 Kafka 生产者,并在 TigerGraph 中实时接收批次事件数据。每轮测试我们都将重启 TigerGraph 以清除缓存影响,Kafka 程序无法准确记录写入速率,为了反映写入速率,我们使用 TigerGraph 本身的计时器,重启时同时也清空了上一轮的计时重新计算写入,多轮对照测试结果取均值,结果几乎无偏差。
在测试过程中发现,Kafka 接口无法启动多个客户端在同一台服务器,也必须是配置使用所有的 Kafka 分区才能启动写入,即默认了接收所有传来的数据,所以 TigerGraph 单节点写入与多客户端没有太大关系,因为已经是使用最大化资源了,所以我们只对单一客户端测试,这与 AbutionGDB,Neo4j,JanusGraph 测试使用的资源几乎是一致的。测试 TigerGraph 数据写入结果截图如下:
结果中,LOADED MESSAGES 表示每批次的实时写入事件记录数(包含两个实体和一条关系),DURATION 表示这批次数据入库的时间。
为了进一步去除网络数据传输等因素影响,我们对每批次写入耗时统一减去 3-4 秒的数据接入响应,包括:Kafka 生产者被接入消费者的时间,一般为 1 秒左右;以及数据实时随机生成耗时,一般为 1 秒左右。最终得到超低误差的实时写入性能如下:
每批插入数: | 1w | 2w | 10w | 20w | 100w | 200w | 500w |
---|---|---|---|---|---|---|---|
入库事件/秒: | 4000 | 8000 | 13333 | 25000 | 50000 | 60606 | 80645 |
上述结果表明,TigerGraph 对于超大规模数据实时写性能表现较好,同时也反映了想要获取更好的性能需要更多的服务器资源。测试中 TigerGraph 的 Kafka 实时写入和离线写入性能最终趋向一致,入库速率与批次入库数量成正比增长。TigerGraph 的写入速度与单请求中记录数目具有极大关系,且随着单请求中记录数目的增加而增加,并始终保持匀速正增长,但增速缓慢。从图中可以看出,TigerGraph 在本测试实时每批次写入 5,000,000 条事件记录和离线每批次写入 500,000 条事件记录时达到最高写入性能,写入速度大约为 80,000 事件记录/秒。
基于以上的测试数据,将各数据库测试出来的最佳速度进行对比,结果如下:
事件数 | 1client | 2client | 3client | 4client | 5client | 6client | 7client |
---|---|---|---|---|---|---|---|
AbutionGDB | 187402 | 229706 | 207413 | 233558 | 263547 | 264271 | 267015 |
Neo4j | 72 | 68 | 63 | 56 | 59 | 55 | 53 |
JanusGraph | 8300 | 7250 | 7220 | 5450 | 5950 | 5800 | 5700 |
TigerGraph | 80645 | 80645 | 80645 | 80645 | 80645 | 80645 | 80645 |
注意:因为 TigerGraph 多客户端实时写性能很差,且变化很小,所以这里使用 Kafka 的实时写加入比较,但又因 Kafka 接入无多客户端,所以将 TigerGraph 每批次写入的最佳性能并入比较,相较于 AbutionGDB,Neo4j,JanusGraph 有一定优势为便于对比,在这里适当不计。
从图中可以看出,AbutionGDB 是 4 个图数据库中唯一一个写入速度随客户端一致线性增加的数据库,且其写入速度远远高于其他三个数据库,也是唯一一个突破十万事件记录/秒写入速度的图数据库。而 TigerGraph、JanusGraph 和 Neo4j 在测试中都展示出了瓶颈。JanusGraph 虽然在客户端较少的情况下就有接近 1w 的事件(约 2.5w 实体和关系)写入速度,但是其速度无法因客户端增加而线性增加,从图可知 JanusGraph 的瓶颈在 1w 事件记录/秒。而 TigerGraph 的实时写不会因为客户端的增加而显著增加写入性能,始终维持在一个低水平状态,但在 Kafka 接口中达到了较好的写入性能,不过只对于大规模量级数据表现较好。对于 Neo4j,其写入性能不会因为客户端的增加而提高,反而略微下降,没有很好的并行性。
综上所述,AbutionGDB 在多客户端连接同步写入的速度远远高于同等条件下的 TigerGraph、JanusGraph 和 Neo4j 等市场主流图数据库。AbutionGDB 在 7 个客户端连接同时写入的情况下可达到~270,000 事件记录/秒的写入速度(约 810,000 条实体与关系),是已测得 JanusGraph 的最大写入速度的 33 倍,是 TigerGraph 普通接口最大写入速度的 222 倍,是 Neo4j 最大写入速度的 3709 倍。
本测试做了简单的查询测试,就是将插入的数据全部读出并做一度关系计算与过滤出邻居数大于 5 的人。因为各大数据库的性能差异很大,为节省测试时间,我们使用一个客户端查询不同的记录条数并进行计算。测试结果如下:
查询响应时间 | AbutionGDB | TigerGraph | JanusGraph | Neo4j |
---|---|---|---|---|
10w | 220 | 210 | 21040(21s) | 12570(12s) |
20w | 212 | 247 | 40780(40s) | 17920(17s) |
50w | 225 | 359 | 106s | 137s |
100w | 306 | 377 | 380s | 441s |
1000w | 679 | 855 | 无法完成 | 无法完成 |
为了方便查看,我们对每个查询耗时转换为统计查询性能:
查询响应时间 | AbutionGDB | TigerGraph | JanusGraph | Neo4j |
---|---|---|---|---|
10w | 455 | 476 | 5 | 8 |
20w | 943 | 810 | 5 | 11 |
50w | 2222 | 1392 | 5 | 4 |
100w | 3268 | 2653 | 3 | 2 |
1000w | 14727 | 11696 | 1 | 1 |
从图表中可以看出,AbutionGDB 的单机最大 OLAP 读取速度在 15,000 记录/毫秒左右,TigerGraph 的最大读取速度在 12,000 记录/毫秒左右,性能几乎相同。在 AbutionGDB 中,实现原理是基于特有的预计算模型,数据在写入的时候就已经自动完成了部分规则统计,查询时仅需少量后计算资源即可得到结果,这与实时数据仓库的特性是类似的。在 TigerGraph 中,因其高级查询语句中支持累加器操作,所以我们针对测试场景定制了一个查询函数,用于累计每个实体的一度关系,它将并行的将函数预先运行到每个实体中,安装函数的步骤会花费几秒钟的时间,以后直接运行查询就会很快,这有点类似 AbutionGDB 的预计算模型机制。
JanusGraph 因为不支持自定义 id,查询起来需要先匹配节点属性做聚合,再做一度关系计算,每次计算量都会比 AbutionGDB 和 TigerGraph 大至少一倍,所以计算速度很慢。而 Neo4j 与 JanusGraph 一样,导入数据时都会自增生成一个 ID 来存储节点,即使相同的事件数据也会重新得到一个新的存储 id,想要加速查询只能对属性添加索引支持,所以聚合类查询都很慢,但因为是原生图存储,数据量少的情况下会比
JanusGraph 略快一些。所以从测试结果来看,AbutionGDB 的查询吞吐量远高于 JanusGraph 和 Neo4j。
综上可以看到,AbutionGDB 写入性能具有良好的横向扩展(scale-out)的能力,对于多节点构成的集群也能提供优秀的扩展支持,而读取性能也具有千万级/秒的速度,对于大规模的大多数数据统计查询场景都能在 1 秒内返回结果。
更多测试持续完善。。。
领取专属 10元无门槛券
私享最新 技术干货