图系统-HugeGraph整体性能测试(三)

上一篇和第一篇已经大致把整个HG的核心结构, 基本特性和特点结合实际”存储+代码”做了基本介绍, 这篇就是HG初识系列的结束篇, 结合实际的生产数据, 给Hugegraph大部分后端做了一个基本的性能测试, 希望可以给想使用HG的同学一个实际参考.

PS: 因为实际数据和机器的不同, 测试结果可能出现很大差异, 有任何问题欢迎留言或邮件联系.

0x00.概述

大体结论:

  1. 在单机10亿数量级的情况, RocksDB可以较好胜任, 性能大幅超出预期. 写入最快可达6亿/h, 读取速度也接近8万/s遍历, 但是单磁盘会频繁掉速, 建议磁盘换为SSD或组RAID. (详见后文官方解释)
  2. Mysql在百万级别查询速度情况良好, 可以有6万/s的遍历速度,单写入对错误数据的容忍性最低. (存疑)
  3. Hbase 在3DataNode测试环境下写入速度良好, 有3亿点/h , 近6亿边/h. 读取速度在一度的时候表现也挺好, 但深度提高读取速度会显著下降.
  4. Cassandra在10亿数量级情况, 写入稳定在1亿/h, 较慢. 奇怪的是读测试目前是相对最慢的.
  5. (对比)JanusGraph使用的Cassandra作为后端, 同样机器下写入速度点1亿/h, 边2亿/h (不写ES)

后面是详细的测试说明..

0x01. 基本环境

A. 硬件环境

Loader和Server都是服务端物理机(1或3台),Loader和Server端分离, 核心配置如下:

  • CPU : 同20物理核 (Xeon E5-2630 )
  • 内存 : 128G内存 (可用30G+) / 314G内存 (可用177G+)
  • 磁盘 : 8T HDD / 2T HDD (系统盘SSD?)
  • 网卡 : 同万兆网卡
  • OS : 同CentOS 7.x

2.21更新: 最近补充测试了一下云虚拟机主机小水管的部署, 双核CPU + 2G内存…. 应该算很低的配置了, 此时要注意直接使用默认的导入参数Server端是会瞬间OOM的. (单台机器Loader和Server和Studio都在一起.)

配置很低的时候, 我是手动调低了batchSize (默认是500条每次) ,改为200条顺畅 (极限在哪没测…), 然后建议手动调低导入线程数 (默认是CPU*2 + 1 ,但是不知道是算的物理核还是逻辑核, 反正手动调为CPU物理核个数比较稳.)

B. 系统环境

  • Hugegraph-Server : 0.8.0
    1. 缓存设置:在测试读性能时需要关闭cache功能, 目前设置把所有相关值设为1 (注意 : 0代表缓存永不超时)
    2. gremlin超时:需要修改gremlin-server.yamlscriptEvaluationTimeout (单位毫秒, 比如10000)
    3. JVM设置: 参考官方. 测试中统一改为31G
  • Hugegraph-Loader : 1.2.0
  • gcc : 4.85+
  • 后端存储:
    1. rocksDB (使用单HDD磁盘)
    2. Mysql 5.6 (使用单HDD磁盘)
    3. Cassandra 3.11 (三节点)
    4. HBase 2.0 (三节点, region server: 3)

C. 测试数据

测试数据包括routedns两个数据源, 其详细结构暂略,总共 6.4亿点 + 7.4亿边, 大致如下:

  • route: 27G数据 (点:1.2亿点 / 边:0.8亿边)

  • dns: 82G数据 (点:5.2亿 / 边:6.6亿)

注意: Mysql因为单表单磁盘的承载能力, 所以需要单独的百万级别的测试数据, Rocksdb / Cassandra /Hbase都是全量的数据导入.

0x02. 写入测试

0. 写入参数

现在采用的是除了Hbase外, 其他默认都开启了二级索引, 注意大量的解析和写入错误可能会很大的影响导入速度, 变为单条写入模式.. 所以之后可能测试结果还会更新一次.

1
bin/hugegraph-loader -g hugegraph -s /path/to/schema.groovy -f /path/to/struct.json  --max-parse-errors 200000 --max-insert-errors 200000 -h 10.203.xx.xx --retry-interval 5 --retry-times 1 --timeout 5

补: 参数默认值参考官方说明. (batchSize和线程数建议不要随便改, 可能卡死)

RocksDB

1.写入结果

这是官方测试rocksDB参考 :

  • 关闭label index,22.8w 边/s
  • 开启label index,15.3w 边/s

目前实测 :

单HDD盘, 综合完整导入时间来看, 顶点4.6万/s, 边4.5万/s, 折合1.6亿/h 顶点或边, 中间会有很明显的卡顿, 或速度急剧偏低 . 问题参考文末 (导入前82G –导入后—> 87G )

注: 因为rocksDB前期或稳定的时候点有 12万/s, 边有20万/s (折合2.5亿/h的点, 7亿/h的边)

1
2
3
4
5
6
7
8
9
10
11
---------------------route------------------------
time results:
2vertices loading time : 228 秒 --> 12839817点
2edges loading time : 2207 秒 --> 81899981边
2total loading time : 2436 秒

--------------------dns----------------------------
time results:
2vertices loading time : 11544 秒 --> 529322279点
2edges loading time : 14464 秒 --> 660014555边
2total loading time : 26009 秒

2. 资源消耗

因为Loader程序资源占用较少, 数据应该也读到了内存中, 所以主要关注Server端 :

  • CPU: 最高2000% , 最低300% , 平均800% 左右 (低负载的时候一般是写入卡住了…)

  • 内存: 30G (需要手动修改server启动配置)

  • 网卡:?MB/s (这个值…待定)

Hbase

采用HBase存储引擎时平均插入点9万/秒,插入边16万/秒 , 速度稳定, 没有明显卡顿 ,还算挺不错.

1
2
3
4
5
6
7
8
9
10
11
---------------------route------------------------
time results:
2vertices loading time : 159 秒 --> 12839817点
2edges loading time : 508 秒 --> 81899981边
2total loading time : 667 秒

--------------------dns-----------------------------
time results:
2vertices loading time : 5395 秒 --> 529322279点
2edges loading time : 6275 秒 --> 660014555边
2total loading time : 11671 秒

Server端资源消耗

  • CPU: 967% (us: 22.6%)
  • 内存:4.7%
  • 网卡:123M/s

Cassandra

Cassandra 目前测试来看, 顶点和边写入正常状态大概3万/s ,折合1亿/h , 导入很稳定 ,容错性最好

1
2
3
4
5
6
7
8
9
10
11
---------------------route------------------------
time results:
2vertices loading time : 393 秒 --> 12839817点
2edges loading time : 2994 秒 --> 81899981边
2total loading time : 3387 秒

--------------------dns----------------------------
time results:
2vertices loading time : 11544 秒 --> 529322279点
2edges loading time : 14464 秒 --> 660014555边
2total loading time : 26009

负载好像忘记记了… 不过没什么特别的地方

Mysql

Mysql因为自身存储结构和单表支持数据量原因, 不能支持上亿数据写入单表. 写入就会反复卡死, 测试数据量需要重新生成一批百万级别的点+边…

所以写入速度还不好写一个具体值, 会经常性的出现Broken pipe 提示. 详见文尾问题区.

JanusGraph

JanusGraph导入数据源与HG一致, 无ES索引 —> 平均写入点或边4万条/秒 (导入方式使用MR/Spark)

1
2
3
4
5
---------------------route------------------------
time results:
2vertices loading time : 900 秒 --> 35235667点(md5点比较耗时)
2edges loading time : 552 秒 --> 175843757边
2total loading time : 1452 秒

因为Janus这里直接走内嵌的core-jar包, 没有走server端, 所以资源消耗就不好对比了, 包括分布式的任worker.

0x03. 读取测试

根据实际的业务需求, 和对读取性能的绝对值评估, 核心采用K步邻居判断, 以及余下补充:

  • 路径查询
  • K步邻居
  • 批量点边

注意: 需要调整HG自带的缓存设置, 以及调整gremlin-server.yaml 里的缓存设置, 不然对查询速度的结果影响很大.

附 : HG自带的扩展查询详细参考官方 , Gremlin查询参考自Tinkerpop文档以及HG官方issue

1.路径查询

1
2
3
4
5
6
7
8
9
10
11
12
13
# 最短路径 (ShortestPath-自带)
http://ip:8080/graphs/hugegraph/traversers/shortestpath?source="2:8.8.8.8"&target="2:223.5.5.5"&max_depth=5&direction=BOTH
http://ip:8080/graphs/hugegraph/traversers/shortestpath?source="1:www.taobao.com"&target="1:tui.aliwatch.com"&max_depth=5&direction=BOTH

# 所有路径 (Paths-自带)
http://ip:8080/graphs/hugegraph/traversers/paths?source="1:www.taobao.com"&target="1:tui.aliwatch.com"&max_depth=5&direction=BOTH

#gremlin使用OLTP方式查最短路径
#注意:limit(-1)表示查找所有路径,下列语句表示只要找到目标顶点或者到达深度为5则停止
http://ip:8080/gremlin?gremlin=hugegraph.traversal().V("2:223.5.5.5").repeat(both().simplePath()).until(hasId("2:8.8.8.8").or().loops().is(gte(5))).hasId("2:8.8.8.8").path().limit(1)

# janusgraph-gremlin
http://ip:8182/?gremlin=g.V().has("data","223.5.5.5").repeat(both().simplePath()).until(has("data","8.8.8.8").or().loops().is(gte(5))).has("data","8.8.8.8").path().limit(1)
查询类型 / 对应后端 RocksDB Hbase Mysql^(*)^ Cassandra JanusGraph
5度最短路 (IP之间) 0.55s 0.65s 0.3s 1.5s /
5度最短路 (域名之间) 2.5s 12s / 11s /
5度全路径查询(域名) 2.7s 13s 0.4s 11s /
5度最短路-Gremlin(IP之间) 0.28s 0.34s / 4s 4s

() : *补充**

  • Mysql数据量是百万级别, 有些数据缺失导致无结果, 其他后端数据源一致.
  • RocksDB同样也存在缓存的问题… 可能是加载到了内存, 目前无法控制…第一次查询和之后的查询时延相差10倍以上..
  • gremlin的测试, 是否gremlin-server自己设置了缓存 ,目前测试看也是有的, 这都会很影响测试多次结果

2. K步邻居

测试的几个case:

1
2
3
4
5
6
7
8
9
# k-neighbor:
http://ip:8080/graphs/hugegraph/traversers/kneighbor?source="2:8.8.8.8"&depth=1&direction=BOTH #case1
http://ip:8080/graphs/hugegraph/traversers/kneighbor?source="1:www.taobao.com"&direction=BOTH&depth=3 #case2

# gremlin:(k-neighbor)
http://ip:8080/gremlin?gremlin=hugegraph.traversal().V('2:8.8.8.8').repeat(__.in()).times(1).dedup()

# janusgraph-Gremlin
http://ip:8182/?gremlin=g.V().hasLabel("dns").has("data","8.8.8.8").both()
查询类型 / 对应后端 RocksDB (时间/数目) Hbase Mysql Cassandra JanusGraph
1层K步邻居 1.3s / 75567个 0.7s / 75567个 / 2.5s/76335个 18s/11526个
2层K步邻居 5.4s / 77834个 42s / 77834个 / 65s/81770个 48s/8486个
3层K步邻居 5.9s / 77834个 43s / 77834个 / 超时 超时
4层K步邻居 / / / /
gremlin - - - -

备注:

  1. gremlin超时 , / 代表暂时无意义 (也不确定是否是超时还是Hugeserver无响应了..)
  2. K-OUT 因为是K步邻居的一部分实现, 所以就不单独列出了.
  3. JanusGraph的查询语句与HG的类似,但二度邻居好像是按照K-out返回的,这里可能需要再调整gremlin写法…

3. 批量点查

1
2
3
4
5
6
7
8
9
10
11
#这个封装实际只是根据顶点ID查询,应该是与hugeGraph的ID构成策略有关,如果利用gremlin直接查,耗时相差不大
http://ip:8080/graphs/hugegraph/traversers/vertices?ids="1:www.taobao.com.cn"&ids="1:sina.cn"&ids="1:www.google.hk"&ids="1:www.google.com.hk"&ids="1:github.com"

#gremlin1(直接查ID):
http://ip:8080/gremlin?gremlin=hugegraph.traversal().V().hasId("1:www.taobao.com.cn","1:sina.cn","1:www.google.hk","1:www.google.com.hk","1:github.com").dedup()

#gremlin2(查属性)这里url就不列了太多
http://ip:8080/gremlin?gremlin=hugegraph.traversal().V().has("dns",within(url1,url2,url....,urln)).dedup()

#janusgraph-gremlin,同样这里ip太多不列了.
http://ip:8182/?gremlin=g.V().hasLabel("dns").has("data",within(var0,var1,var2,var3,var4,var5,var6,var7,var8,var9)).local(inE("A").limit(50)).dedup().store("E").bothV().dedup().store("V").cap("V","E")&bindings.var0=ip1&&bindings.var1=ip2........&bindings.var9=ip10
查询类型 / 对应后端 RocksDB (时间/数目) Hbase Mysql Cassandra JanusGraph(*)
批量点查 0.03s / 4个 0.11s 0.02s 0.06s / 4个 /
批量边查 0.05s / 3个 0.8s 0.05s / 3个 /
批量属性差 1.5s/10个
gremlin

(*):JanusGraph因为ID的构成方式,无法根据顶点ID直接查点/边,只能查属性

补充 :

  • 同样Rocksdb第一次查平均0.1s . 后续平均30ms, 各后端差别不大, 稍快于gremlin查询.
  • 测试个位数的点查意义很小, 几乎体现不出区别, 后续应该补上批量查万级别的点, 通过Shard 获取一批点/边ID
  • 主要用途应该也是为了便于最短路/K步邻居的顶点ID批量传入查询具体数据… (推测..)

0x04.存在的问题

Rocksdb , Mysql , Cassandra 导入的时候都可能出现很严重的问题, 导致写入中断, 或者速度大幅降低, 或完全卡死,这里列一下, 仅供参考

1.RocksDB写入相关问题

这个单磁盘持续写入几乎必会出现, 但是引用官方解释之后觉得大部分情况可以说得通, 后续需要注意:

RocksDB后期导入速度变慢可能的原因很多,下面是常见的几种:

  1. 批量插入失败时(比如某一条数据不符合规范),loader会尝试以单条模式进行重试,如果批量插入的非法数据过多可能导致后期缓慢;(我们符合)
  2. 使用单块磁盘持续导入过多数据时,RocksDB因为compaction可能导致速度变慢(write stall ) ( 需要确认)
  3. JVM分配内存不足可能导致速度变慢。 (需要确认, 目前应该是默认给的18G)

解决方案:

  1. 对于case 1,请看看日志目录中的错误内容是否过多,以及后期是否进度每500条变动一次(我们不是)。针对该问题我们后续会改进loader,请关注:hugegraph/hugegraph-loader#11
  2. 对于case 2,请查看rocksdb日志来确认,需要具体分析哪一部分数据导致缓慢进而进行优化 , 后续可以LVM+RAID分摊写入 (日志看不懂…)
  3. 对于case 3,可通过调用任意查询API来查看是否server响应缓慢 (需要验证)

总之官方说的已经挺全面详细了, 如果还有其他问题再跟进.

2. Mysql写入相关问题

  • 需要调整默认的100个连接数, 否则很快会超过, 建议至少改为1000 ,修改方法参考

  • 需要尽量减少不合法的数据, 否则会出现大量的broken pipe–jason 解析错误 ,具体原因未知

    hgMsErr00

    这样就会导致有大量的网络阻塞和停顿:

    hgMsErr01

3. Cassandra读写相关问题

可以说Cassandra如果遇到了很明显的卡顿, 或者从一开始就Loader的显示就每500/s 很缓慢的变动, 都是Cassandra配置 + 代码修改的问题 (跟HG基本无关, 请再三确认)

  • 确认Schema是否在所有节点完全同步( 初始化的时间长达数分钟,如果时间过短很可能二级索引没有完全构建好, 很难发现), 正常情况Cassandra写入很稳定, 几乎不会出现任何卡顿
  • 确认Cassandra的配置是否合理, 如果不确定建议先使用官方配置.
  • Cassandra目前读取速度是后端里最慢的. 具体原因可能还需要确定

4. HBase读写相关问题

  • Failed to do request: 用loader进行数据导入时出现报错com.baidu.hugegraph.rest.ClientException: Failed to do request官方解释是由于短时间内大量建立tcp连接导致的, 可以尝试开启Linux内核参数(vi /etc/sysctl.conf):
1
2
3
4
5
6
7
8
net.ipv4.tcp_tw_reuse = 1 #表示重用TIME-WAIT socket
net.ipv4.tcp_tw_recycle = 1 #表示回收TIME-WAIT socket

#修改完后使它立即生效
/sbin/sysctl -p

#可以通过检查TCP状态观察是否有改善
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

还有一些问题, 但是好像有点遗忘了… 想起来再补充把, 核心的应该是这些, 主要是测试的时候多级的缓存(包括内存)的干扰会严重影响测试结果, 务必注意统一测试环境..


参考资料:

  1. 官方Loader文档
  2. rest-api查看查询时延和日志的方式#issue
  3. Cent7-install-ms
  4. Too many connections in ms