扫码关注公众号:芋道源码

发送: 百事可乐
获取永久解锁本站全部文章的链接

《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Performance-Testing/MongoDB-benchmark/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

1. 概述

对于很多开发者,MongoDB 是一种文档型的 NoSQL 数据库,实际场景可能并没有使用过。按照熟悉的程度,可能是 MySQL > Redis > Elasticsearch > MongoDB ,当然这不一定准确,只是艿艿自己的猜测。

MongoDB 自 2007 年被 10gen 团队所开发,2009 年开源,一度风光到号称“取代”关系数据库,成为新一代的数据库霸主。不过可能也并没有那么夸张。

艿艿使用 MongoDB ,应该是 2011 年的时候,当时处于 1.x 左右的版本,因为当时公司 MySQL 碰到很大的性能瓶颈,并且暂时找不到合适的方案,后来经过测试,逐步替换 MongoDB 成为主数据库支撑业务。那个年代,MySQL 提供的高性能解决方案,不像现在这么丰盛。

发展到目前,MongoDB 已经成功上市,并且在国内外在一些业务场景被重度使用,也算盘踞一方。并且,在 4.0 版本,又提供了事务的特性,仿佛又是一个新的故事的开始。

哈哈,废话了这么多,我们还是进入整体,来对 MongoDB 进行性能基准测试。在开始基准测试之前,我们比较快捷的知道,MongoDB 大体的性能规格,从各大云厂商提供的 MongoDB 云服务。

艿艿:哈哈哈哈,老套路,用起来。

2. 性能指标

通过我们看各大厂商提供的指标,我们不难发现,主要是 3 个指标:

  • throughput :客户端测试吞吐,即:读写操作数。
  • RAL :读操作平均延迟(us)。

如果类似 《性能测试 —— MySQL 基准测试》 的话,如下:

MySQL MongoDB
QPS throughput
TPS MongoDB 4.0 版本增加了事务,但是本文暂时先不测试。
RT RAL、RAL
Concurrency Threads 暂无

当然,本文使用的测试工具,和 《性能测试 —— MySQL 基准测试》 不同,所以测试出来的性能结果,不能直接拿来做对比。更多的目的是,在于对比不同硬件下,MongoDB 的基准性能。例如说:阿里云最近出了 ESSD 云盘,而我们之前使用的是 SSD 云盘 ,想测试下,变更后,能带来多大的性能提升。

艿艿:不了解阿里云的云盘的胖友,可以看看 《阿里云高效云盘和SSD云盘有什么区别?哪个更好?》 文章。

3. 测试工具

相比 MySQL 来说,MongoDB 的测试工具是比较不用挑选的,直接使用 YCSB 就完事了,并且各大云产商提供的性能基准测试,也非常一致的选择了它。

4. YCSB

FROM 《雅虎云服务基准测试工具 YCSB》

Yahoo! Cloud Serving Benchmark (YCSB) 是 Yahoo 公司的一个用来对云服务进行基础测试的工具。

实际上,我们打开 YCSB 仓库,也会发现,它不仅仅支持 MongoDB 的基准性能测试,也支持 HBase、JDBC(所以也支持 MySQL、Oracle 等等)、Elasticsearch 等等数据库服务。

😈 以后有时间,拿 YCSB 测试下 MySQL ,这样好和 MongoDB 有相同的对比。

4.1 测试环境

艿艿:下面,我们拿一台阿里云的 ECS 服务器,进行测试。

《性能测试 —— MySQL 基准测试》 使用的是同一台服务器。

  • 型号 :ecs.c5.xlarge

    艿艿:和我一样抠门(穷)的胖友,可以买竞价类型服务器,使用完后,做成镜像。等下次需要使用的时候,恢复一下。HOHO 。

  • 系统 :CentOS 7.6 64位

  • CPU :4 核
  • 内存 :8 GB
  • 磁盘 :40 GB ESSD 云盘

    艿艿:“土豪”一点的胖友,可以买更大的磁盘容量,因为越大的容量,越高的 IOPS 。

  • MongoDB :version v3.6.12

下面,我们需要开始安装如下软件,以完成测试流程。大体上,我们会参考 YCSB - mongodb - Quick Start 文章。

  • MongoDB
  • JDK
  • Maven
  • YCSB

4.2 安装 MongoDB

# 下载
$ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.6.12.tgz

# 解压
$ tar -zxvf mongodb-linux-x86_64-3.6.12.tgz
$ cd mongodb-linux-x86_64-3.6.12

# 启动
$ mkdir /tmp/mongodb
$ nohup bin/mongod --dbpath /tmp/mongodb &

4.3 安装 JDK

# 安装 JDK
$ sudo yum install java-devel

# 查看版本
$ java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (build 1.8.0_212-b04)
OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)

4.4 安装 Maven

# 安装 Maven
$ sudo yum install maven

# 查看版本
$ mvn -version
Apache Maven 3.0.5 (Red Hat 3.0.5-17)

4.5 安装 YCSB

打开 YCSB releases ,我们可以下载 YCSB 测试工具。目前艿艿看到最新的版本是 0.15.0 版本。

YCSB 不仅仅可以测试 MongoDB ,也可以测试其他服务,所以它提供了每个服务的单独测试的包,也提供了全量的测试的包。我们只需要测试 MongoDB ,因而只要下载 MongoDB 服务的测试的包即可。

# 下载 YCSB MongoDB
$ wget https://github.com/brianfrankcooper/YCSB/releases/download/0.15.0/ycsb-mongodb-binding-0.15.0.tar.gz

# 解压
$ tar -zxvf ycsb-mongodb-binding-0.15.0.tar.gz

# 查看目录
$ cd ycsb-mongodb-binding-0.15.0
$ ls -lh
total 36K
-rw-r--r-- 1 502 games 12K May 20 2018 LICENSE.txt
-rw-r--r-- 1 502 games 615 May 20 2018 NOTICE.txt
-rw-r--r-- 1 502 games 5.6K May 20 2018 README.md
drwxr-xr-x 2 root root 4.0K May 23 15:45 bin
drwxr-xr-x 2 root root 4.0K May 23 15:45 lib
drwxr-xr-x 2 502 games 4.0K Jul 7 2018 workloads

如下内容,参考自 《使用 YCSB 测试 mongodb》 文章。

  • bin :目录下有个可执行的 ycsb 文件,是个 python 脚本,是用户操作的命令行接口。ycsb 主逻辑是:解析命令行,设置 Java 环境,加载java-libs ,封装成可以执行的 Java 命令,最后执行。
  • workloads :目录下有各种 workload 的模板,可以基于 workload 模板进行个性化修改。
  • lib :包含 ycsb 里各种核心实现,比如 DB 的抽象类 DB.java ,各个 db 子类都要继承该类;还有比如 workload 抽象类,如果我们要自定义 workload 实现也需要继承该类。

4.6 使用指南

和大多数性能测试工具一样,YCSB 分成两个接口:

  • load 阶段:加载,准备数据。
  • run 阶段:执行,测试性能。

4.6.1 load 阶段

bin/ycsb load mongodb -s -P workloads/workloada -threads 100 > outputRun.txt
  • load :表示使用 load 阶段,YCSB 会根据 workload 模板,往 MongoDB 中,自动生成测试数据,并插入到其中。
  • mongodb :表示我们测试的是 MongoDB ,使用同步模式。什么意思?实际上,YCSB MongoDB 提供了 mongodb 和 mongodb-async 两种模式:
    • mongodb :默认,表示使用同步的方式,YCSB 会调用 mongodb/srcMongodbClient.java 的实现。
    • mongodb-async :表示使用异步的方式,YCSB 会调用 mongodb/srcAsyncMongoDbClient.java 的实现。
    • 为什么会有这样的设计?不同于 MySQL Driver ,MongoDB 提供了 MongoDB DriverMongoDB Driver Async 两种。当然,实际情况,我们更多使用 MongoDB Driver 同步驱动,所以可以暂时无视 mongodb-async 模式。并且,艿艿看了阿里云和华为云,都是使用同步的模式哈~
  • -s :打印状态日志到控制台上。如果不添加,则代表关闭,不输出状态日志到控制台上。一般情况下,建议加上。
  • -P :使用的 workload 模板的地址。具体的 workload 模板说明,我们放在 「4.6.3 workload 模板」 中。
    • -p key=value :指定 key、value 配置,覆盖 workload 模板的配置。
  • -threads :客户端数,每个客户端代表一个线程。
  • > :将执行的结果,输出到指定目录。我们来看个输出结构:

    mongo client connection created with mongodb://localhost:27017/ycsb?w=1
    [OVERALL], RunTime(ms), 57468
    [OVERALL], Throughput(ops/sec), 17400.988376139765
    [TOTAL_GCS_PS_Scavenge], Count, 79
    [TOTAL_GC_TIME_PS_Scavenge], Time(ms), 449
    [TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.7813043780886755
    [TOTAL_GCS_PS_MarkSweep], Count, 0
    [TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
    [TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
    [TOTAL_GCs], Count, 79
    [TOTAL_GC_TIME], Time(ms), 449
    [TOTAL_GC_TIME_%], Time(%), 0.7813043780886755
    [CLEANUP], Operations, 100
    [CLEANUP], AverageLatency(us), 67.23
    [CLEANUP], MinLatency(us), 1
    [CLEANUP], MaxLatency(us), 6499
    [CLEANUP], 95thPercentileLatency(us), 3
    [CLEANUP], 99thPercentileLatency(us), 9
    [INSERT], Operations, 1000000
    [INSERT], AverageLatency(us), 5586.622735
    [INSERT], MinLatency(us), 102
    [INSERT], MaxLatency(us), 955391
    [INSERT], 95thPercentileLatency(us), 11695
    [INSERT], 99thPercentileLatency(us), 62495
    [INSERT], Return=OK, 1000000
    • Throughput 为 17400/s ,平均延迟是 5586us = 5.586 ms 。
    • 这里基本看到的是写入的指标,其它的,胖友自己瞅瞅。

❓ 可能胖友会有疑惑,这里怎么没有填写 MongoDB 服务的地址呢?因为艿艿是在 MongoDB 所在的服务器,而 YCSB 会默认连接连接的地址是 127.0.0.1:27017 ,所以就能连上 MongoDB 服务。并且,默认情况下,连接的是 ycsb 库。如果胖友想要设置 MongoDB 服务的地址,可以添加 -p mongodb.url=mongodb://ip:port/db

  • ip :指定 MongoDB 服务的地址。
  • port :指定 MongoDB 服务的端口。
  • db :指定 MongoDB 服务的数据库。

4.6.2 run 阶段

bin/ycsb run mongodb -s -P workloads/workloada -threads 100 > outputRun.txt
  • run :表示使用 run 阶段,YCSB 会根据 workload 模板,执行相应操作的性能测试。
  • 其它参数,和我们在 「4.6.1 load 阶段」 说的是一样的都。
  • > :将执行的结果,输出到指定目录。我们来看个输出结构:

    mongo client connection created with mongodb://localhost:27017/ycsb?w=1
    [OVERALL], RunTime(ms), 82048
    [OVERALL], Throughput(ops/sec), 12187.98751950078
    [TOTAL_GCS_PS_Scavenge], Count, 96
    [TOTAL_GC_TIME_PS_Scavenge], Time(ms), 463
    [TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.5643038221528861
    [TOTAL_GCS_PS_MarkSweep], Count, 0
    [TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
    [TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
    [TOTAL_GCs], Count, 96
    [TOTAL_GC_TIME], Time(ms), 463
    [TOTAL_GC_TIME_%], Time(%), 0.5643038221528861
    [READ], Operations, 500369
    [READ], AverageLatency(us), 4803.84743059622
    [READ], MinLatency(us), 121
    [READ], MaxLatency(us), 3493887
    [READ], 95thPercentileLatency(us), 13159
    [READ], 99thPercentileLatency(us), 40575
    [READ], Return=OK, 500369
    [CLEANUP], Operations, 100
    [CLEANUP], AverageLatency(us), 196.59
    [CLEANUP], MinLatency(us), 0
    [CLEANUP], MaxLatency(us), 19471
    [CLEANUP], 95thPercentileLatency(us), 3
    [CLEANUP], 99thPercentileLatency(us), 10
    [UPDATE], Operations, 499631
    [UPDATE], AverageLatency(us), 11328.517980269438
    [UPDATE], MinLatency(us), 134
    [UPDATE], MaxLatency(us), 3690495
    [UPDATE], 95thPercentileLatency(us), 28751
    [UPDATE], 99thPercentileLatency(us), 103807
    [UPDATE], Return=OK, 499631
    • Throughput 为 12187/s 。
    • 因为我们这里只配置了读取和更新,所以我们可以看到 [READ][UPDATE] 有相应的数据。

4.6.3 workload 模板

艿艿:英语比较好的同学,可以看看 YCSB - Core Workloads 文档。

recordcount=1000000 # 单表的记录总数
operationcount=1000000 # 总共的操作次数。在本文,它代表向数据发起操作的总次数。
workload=com.yahoo.ycsb.workloads.CoreWorkload

readallfields=true # 是否读取所有字段。

readproportion=0.5 # 读操作的比例
updateproportion=0.5 # 更新操作的比例
scanproportion=0 # 遍历操作的比例
insertproportion=0 # 插入操作的比例

这里写的比较简单,感兴趣的胖友,可以看看 《YCSB 官方文档 - 核心属性(中译)》

4.7 测试结果

下面,我们要进行阿里云 ESSD 和 SSD 云盘下的性能测试对比。

4.7.1 第一轮

使用的磁盘大小,为 40GB :

  • SSD 云盘:提供 3000 IOPS
  • ESSD 云盘:提供 3800 IOPS

使用的 workload 模板如下:

recordcount=1000000 # 单表的记录总数
operationcount=10000000 # 总共的操作次数。在本文,它代表向数据发起操作的总次数。
workload=com.yahoo.ycsb.workloads.CoreWorkload

readallfields=true # 是否读取所有字段。

readproportion=0.9
updateproportion=0.1
scanproportion=0
insertproportion=0
  • 单表 100w 记录,执行 1000w 次操作。
  • 读写比例,按照 9:1 。

另外,执行的线程数(--threads)是 100 。

执行的 load 和 run 的命令,就是我们在上面 「4.6 使用指南」 上提供的两个命令示例。

还有,因为整个测试过程中占用 CPU 消耗比较大,所以艿艿又另外开了一台服务器,进行执行 YCSB 命令。也就是说,服务器 A 执行 YCSB 命令,服务器 B 运行 MongoDB 服务。

再补充一点,默认情况下,MongoDB 服务默认值监听本地 127.0.0.1 地址,所以导致远程服务无法连接,所以艿艿新增了 /etc/mongodb.conf 文件,允许远程连接。如下:

net:
port: 27017
bindIpAll: true # 设置绑定本地所有 IP ,从而远程可以连接。

storage:
dbPath: /tmp/mongodb
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 7 # 设置最大使用内存为 7GB 。

4.7.1.1 SSD 云盘

mongo client connection created with mongodb://172.19.83.153:27017/ycsb
[OVERALL], RunTime(ms), 334886
[OVERALL], Throughput(ops/sec), 29860.90789104352
[TOTAL_GCS_PS_Scavenge], Count, 1262
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 2701
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.8065431221370856
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 1262
[TOTAL_GC_TIME], Time(ms), 2701
[TOTAL_GC_TIME_%], Time(%), 0.8065431221370856
[READ], Operations, 4738022
[READ], AverageLatency(us), 2810.9622703313744
[READ], MinLatency(us), 168
[READ], MaxLatency(us), 9322495
[READ], 95thPercentileLatency(us), 4423
[READ], 99thPercentileLatency(us), 9471
[READ], Return=OK, 4738022
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 58.96
[CLEANUP], MinLatency(us), 0
[CLEANUP], MaxLatency(us), 5663
[CLEANUP], 95thPercentileLatency(us), 11
[CLEANUP], 99thPercentileLatency(us), 16
[UPDATE], Operations, 5261978
[UPDATE], AverageLatency(us), 3803.2724764337668
[UPDATE], MinLatency(us), 185
[UPDATE], MaxLatency(us), 5980159
[UPDATE], 95thPercentileLatency(us), 5155
[UPDATE], 99thPercentileLatency(us), 13375
[UPDATE], Return=OK, 5261978
  • 总体的,Throughput 为 29860/s 。
  • 读操作,平均延迟为 2810us = 2.810ms 。
  • 写操作,平均延迟为 3803us = 3.803ms 。

4.7.1.2 ESSD 云盘

mongo client connection created with mongodb://172.19.83.152:27017/ycsb
[OVERALL], RunTime(ms), 346498
[OVERALL], Throughput(ops/sec), 28860.19544124353
[TOTAL_GCS_PS_Scavenge], Count, 1206
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 2787
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.8043336469474572
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 1206
[TOTAL_GC_TIME], Time(ms), 2787
[TOTAL_GC_TIME_%], Time(%), 0.8043336469474572
[READ], Operations, 4735713
[READ], AverageLatency(us), 2988.817985169287
[READ], MinLatency(us), 157
[READ], MaxLatency(us), 5496831
[READ], 95thPercentileLatency(us), 4719
[READ], 99thPercentileLatency(us), 9911
[READ], Return=OK, 4735713
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 88.26
[CLEANUP], MinLatency(us), 0
[CLEANUP], MaxLatency(us), 8735
[CLEANUP], 95thPercentileLatency(us), 2
[CLEANUP], 99thPercentileLatency(us), 7
[UPDATE], Operations, 5264287
[UPDATE], AverageLatency(us), 3859.884884695686
[UPDATE], MinLatency(us), 168
[UPDATE], MaxLatency(us), 5591039
[UPDATE], 95thPercentileLatency(us), 5263
[UPDATE], 99thPercentileLatency(us), 12087
[UPDATE], Return=OK, 5264287
  • 总体的,Throughput 为 28860/s 。
  • 读操作,平均延迟为 157us = 0.157ms 。
  • 写操作,平均延迟为 3859us = 3.859ms 。

4.7.1.3 对比总结

艿艿:因为一开始用本机执行 YCSB 命令,结果跑下来,有点意外,MongoDB 在 SSD 云盘的性能,竟然好于 ESSD 云盘的性能,虽然相差的实际并不多。这个是为什么呢?看了下系统监控,看到 CPU 基本跑满,而 IOPS 基本都是很低,才几百,所以猜测是 CPU 导致的瓶颈。这也是,为什么后续我改成了非本机跑 YCSB 命令的原因。

从目前数据看下来,在 40G 磁盘大小的情况下,ESSD 和 SSD 的 Throughput 是基本差不多的,虽然很奇怪的 ESSD 低于 SSD ,但是无伤大雅。这个到底是什么原因呢?

  • 通过监控磁盘的 IOPS ,发现只有几百左右,甚至后续都是几十,完全跑到应有的 IOPS 。
  • 在 MongoDB 的 ycsb 库中,执行 db.stats() 命令,结果如下:

    > db.stats()
    {
    "db" : "ycsb",
    "collections" : 1,
    "views" : 0,
    "objects" : 1000000,
    "avgObjSize" : 1167.879874,
    "dataSize" : 1167879874,
    "storageSize" : 3343339520,
    "numExtents" : 0,
    "indexes" : 1,
    "indexSize" : 27283456,
    "fsUsedSize" : 11422699520,
    "fsTotalSize" : 42135011328,
    "ok" : 1
    }
    • 不了解这个指标的同学,可以先了解下 《关于 mongodb 的db.stats()》 文章。
    • 重点来看 dataSize 指标,换算单位就是 1.08GB 大小。这意味着什么?全部数据妥妥的全部放到内存之中,这导致完全无法看出 IOPS 在测试过程中的用途。
    • 还有一个 indexSize 指标,换算单位就是 26M 大小。这个是很重要的,MongoDB 实际使用时,需要保证索引都在内存中。

那么怎么办呢?进入第二轮,答案揭晓。

4.7.2 第二轮

基于第一轮的“失利”,我们需要想办法,避免数据全部缓存到内存中,毕竟这个和我们实际场景是有差别的。所以,我们准备做如下几件事情:

  • 降低 MongoDB 使用的内存大小,改成 4GB 。配置如下:

    net:
    port: 27017
    bindIpAll: true # 设置绑定本地所有 IP ,从而远程可以连接。

    storage:
    dbPath: /tmp/mongodb
    journal:
    enabled: true
    wiredTiger:
    engineConfig:
    cacheSizeGB: 1 # 设置最大使用内存为 1GB 。
    • MongoDB 最小内存要求是 1GB 。
  • 增加 MongoDB 测试的数据量,提升数据量为 1000w ,这样预计 dataSize 就达到 10G 的级别,超过内存的大小。workload 模板如下:

    recordcount=10000000 # 单表的记录总数
    operationcount=100000 # 总共的操作次数。在本文,它代表向数据发起操作的总次数。
    workload=com.yahoo.ycsb.workloads.CoreWorkload

    readallfields=true # 是否读取所有字段。

    readproportion=0.9
    updateproportion=0.1
    scanproportion=0
    insertproportion=0
    • recordcount 从 100w 增加到 1000w 。
    • operationcount 从 1000w 降低到 10w ,因为 Throughput 会下降非常多,所以减少操作数,不然要等好久,嘿嘿。

因为我们之前已经在 MongoDB 中初始化好了测试数据,所以需要先去删除掉哪批数据。操作流程如下:

# 连接 MongoDB
bin/mongo

# 选择数据库
use ycsb

# 删除数据
db.usertable.drop()

然后在重新 YCSB run 命令,重新初始化数据吧。

4.7.2.1 SSD 云盘

mongo client connection created with mongodb://172.19.83.153:27017/ycsb
[OVERALL], RunTime(ms), 57564
[OVERALL], Throughput(ops/sec), 1737.1968591480786
[TOTAL_GCS_PS_Scavenge], Count, 21
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 84
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.14592453616843862
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 21
[TOTAL_GC_TIME], Time(ms), 84
[TOTAL_GC_TIME_%], Time(%), 0.14592453616843862
[READ], Operations, 47758
[READ], AverageLatency(us), 55078.55559278027
[READ], MinLatency(us), 178
[READ], MaxLatency(us), 3205119
[READ], 95thPercentileLatency(us), 205695
[READ], 99thPercentileLatency(us), 422143
[READ], Return=OK, 47758
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 310.99
[CLEANUP], MinLatency(us), 0
[CLEANUP], MaxLatency(us), 30975
[CLEANUP], 95thPercentileLatency(us), 3
[CLEANUP], 99thPercentileLatency(us), 8
[UPDATE], Operations, 52242
[UPDATE], AverageLatency(us), 55947.62777075916
[UPDATE], MinLatency(us), 198
[UPDATE], MaxLatency(us), 3213311
[UPDATE], 95thPercentileLatency(us), 205439
[UPDATE], 99thPercentileLatency(us), 421375
[UPDATE], Return=OK, 52242
  • 总体的,Throughput 为 1737/s 。
  • 读操作,平均延迟为 55078us = 55.078ms 。
  • 写操作,平均延迟为 55947us = 55.947ms 。

4.7.2.2 ESSD 云盘

mongo client connection created with mongodb://172.19.83.152:27017/ycsb
[OVERALL], RunTime(ms), 55237
[OVERALL], Throughput(ops/sec), 1810.3807230660607
[TOTAL_GCS_PS_Scavenge], Count, 30
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 97
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.17560693013740789
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 30
[TOTAL_GC_TIME], Time(ms), 97
[TOTAL_GC_TIME_%], Time(%), 0.17560693013740789
[READ], Operations, 90086
[READ], AverageLatency(us), 52376.83751082299
[READ], MinLatency(us), 179
[READ], MaxLatency(us), 1799167
[READ], 95thPercentileLatency(us), 204543
[READ], 99thPercentileLatency(us), 304127
[READ], Return=OK, 90086
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 61.98
[CLEANUP], MinLatency(us), 0
[CLEANUP], MaxLatency(us), 6063
[CLEANUP], 95thPercentileLatency(us), 5
[CLEANUP], 99thPercentileLatency(us), 16
[UPDATE], Operations, 9914
[UPDATE], AverageLatency(us), 54254.13738148074
[UPDATE], MinLatency(us), 228
[UPDATE], MaxLatency(us), 1798143
[UPDATE], 95thPercentileLatency(us), 206591
[UPDATE], 99thPercentileLatency(us), 303871
[UPDATE], Return=OK, 9914
  • 总体的,Throughput 为 1810/s 。
  • 读操作,平均延迟为 52376us = 52.376ms 。
  • 写操作,平均延迟为 54254us = 54.254ms 。

4.7.2.3 对比总结

执行的结果,40G 磁盘大小的情况下,SSD 与 ESSD 云盘,带来的吞吐量基本也相差不大。我的想法是:

  • IOPS 3000 与 3800 的情况下,带给 MongoDB 不会有想象中这么大的性能差距。
  • YCSB 每次的测试结果,还是有一定差距的,ESSD 云盘,最差的时候,Throughput 才 1400/s 。

不过,我们也会发现另外一个现象,MongoDB 对内存的依赖是非常大的,一旦频繁读取硬盘,Throughput 简直是急剧下滑,扎心~

当然,艿艿还是不信这个邪恶,进入我们的第三轮测试。

艿艿:还有一点要注意,一定一定。因为初始时,MongoDB 内存并未使用,所以初期的性能,很多是来源于内存。所以要多跑几次,以最后几次为准。

4.7.3 第三轮

假装有金主爸爸的捐款,我们来使用的更大的磁盘,为 100GB :

  • SSD 云盘:提供 4800 IOPS
  • ESSD 云盘:提供 6800 IOPS

从阿里云官方文档也可以看到,ESSD 很大的优势,在于每 GB 带来了更多的 IOPS 。

不唠了,直接开始继续搞。当然,还是有一点要注意,因为 IOPS 的提升,我们将修改 workload 模板,提升操作次数:

recordcount=10000000 # 单表的记录总数
operationcount=200000 # 总共的操作次数。在本文,它代表向数据发起操作的总次数。
workload=com.yahoo.ycsb.workloads.CoreWorkload

readallfields=true # 是否读取所有字段。

readproportion=0.9
updateproportion=0.1
scanproportion=0
insertproportion=0
  • operationcount 从 10w 提升到 20w 。

4.7.3.1 SSD 云盘

mongo client connection created with mongodb://172.19.83.155:27017/ycsb
[OVERALL], RunTime(ms), 94870
[OVERALL], Throughput(ops/sec), 2108.1479919890376
[TOTAL_GCS_PS_Scavenge], Count, 59
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 155
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.16338146937915043
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 59
[TOTAL_GC_TIME], Time(ms), 155
[TOTAL_GC_TIME_%], Time(%), 0.16338146937915043
[READ], Operations, 94309
[READ], AverageLatency(us), 46188.52209227115
[READ], MinLatency(us), 181
[READ], MaxLatency(us), 2916351
[READ], 95thPercentileLatency(us), 195071
[READ], 99thPercentileLatency(us), 503295
[READ], Return=OK, 94309
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 809.8
[CLEANUP], MinLatency(us), 0
[CLEANUP], MaxLatency(us), 80831
[CLEANUP], 95thPercentileLatency(us), 3
[CLEANUP], 99thPercentileLatency(us), 17
[UPDATE], Operations, 105691
[UPDATE], AverageLatency(us), 45863.72863346926
[UPDATE], MinLatency(us), 181
[UPDATE], MaxLatency(us), 2906111
[UPDATE], 95thPercentileLatency(us), 195071
[UPDATE], 99thPercentileLatency(us), 497919
[UPDATE], Return=OK, 105691
  • 总体的,Throughput 为 2108/s 。
  • 读操作,平均延迟为 46341us = 46.341ms 。
  • 写操作,平均延迟为 45863us = 45.863ms 。

4.7.3.2 ESSD 云盘

mongo client connection created with mongodb://172.19.83.154:27017/ycsb
[OVERALL], RunTime(ms), 95166
[OVERALL], Throughput(ops/sec), 2101.590904314566
[TOTAL_GCS_PS_Scavenge], Count, 23
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 90
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.09457159069415548
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 23
[TOTAL_GC_TIME], Time(ms), 90
[TOTAL_GC_TIME_%], Time(%), 0.09457159069415548
[READ], Operations, 94795
[READ], AverageLatency(us), 45919.30552244317
[READ], MinLatency(us), 167
[READ], MaxLatency(us), 2314239
[READ], 95thPercentileLatency(us), 196095
[READ], 99thPercentileLatency(us), 406015
[READ], Return=OK, 94795
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 149.28
[CLEANUP], MinLatency(us), 0
[CLEANUP], MaxLatency(us), 14735
[CLEANUP], 95thPercentileLatency(us), 3
[CLEANUP], 99thPercentileLatency(us), 13
[UPDATE], Operations, 105205
[UPDATE], AverageLatency(us), 46341.21347844684
[UPDATE], MinLatency(us), 176
[UPDATE], MaxLatency(us), 2312191
[UPDATE], 95thPercentileLatency(us), 195967
[UPDATE], 99thPercentileLatency(us), 408063
[UPDATE], Return=OK, 105205
  • 总体的,Throughput 为 2101/s 。
  • 读操作,平均延迟为 45919us = 45.919ms 。
  • 写操作,平均延迟为 46341us = 46.341ms 。

4.7.3.3 对比总结

扎扎扎心啊,貌似性能上木有太大差距?!又重新看了下 阿里云 ESSD 云盘 的介绍,差距应该还是蛮大呀。感觉我肯定踩了什么坑。

内牛满面。如果了解的胖友,欢迎来指导下艿艿。😈

但是等等,又重新看了下监控,看到 Prometheus 监控 MongoDB 所在的主机,磁盘读取峰值在 167 MB/s ,有点猜测是不是有可能和这个指标有关系。并且,我们在 《阿里云 - 块存储常见问题》 看到,ECS 的型号,会影响 ECS 的读写吞吐量。如下图所示:ECS 与吞吐的关系

  • 我们使用的阿里云 ECS 机型 ecs.c5.xlarge ,应该属于表格中 ecs.g5se.xlarge 这个规格,它的峰值存储吞吐量在 235 MB/s ,所以我们准备尝试下,购买 ecs.g5se.2xlarge 规格,有 470 MB/S 的吞吐量,表现会不会好一些。So 进入第四轮的测试啦。

4.7.4 第四轮

再次假装有金主爸爸的捐款,我们购买了阿里云 ECS ecs.c5.2xlarge 型号,8 核 16GB 内存。主要的重点是,有更大的读写吞吐量 470 MB/S 。

艿艿:虽然说 CPU 和内存有了 Double 的提升,但是应该对结果影响不大。

啥也不唠,继续开始。

4.7.4.1 SSD 云盘

mongo client connection created with mongodb://172.19.83.157:27017/ycsb
[OVERALL], RunTime(ms), 718323
[OVERALL], Throughput(ops/sec), 2784.262789859158
[TOTAL_GCS_PS_Scavenge], Count, 1262
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 2372
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.33021356687729614
[TOTAL_GCS_PS_MarkSweep], Count, 1
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 27
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0037587547663098632
[TOTAL_GCs], Count, 1263
[TOTAL_GC_TIME], Time(ms), 2399
[TOTAL_GC_TIME_%], Time(%), 0.333972321643606
[READ], Operations, 947019
[READ], AverageLatency(us), 35419.04046803707
[READ], MinLatency(us), 173
[READ], MaxLatency(us), 9215999
[READ], 95thPercentileLatency(us), 194687
[READ], 99thPercentileLatency(us), 600575
[READ], Return=OK, 947019
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 60.33
[CLEANUP], MinLatency(us), 0
[CLEANUP], MaxLatency(us), 5843
[CLEANUP], 95thPercentileLatency(us), 3
[CLEANUP], 99thPercentileLatency(us), 8
[UPDATE], Operations, 1052981
[UPDATE], AverageLatency(us), 35384.690031444065
[UPDATE], MinLatency(us), 180
[UPDATE], MaxLatency(us), 9043967
[UPDATE], 95thPercentileLatency(us), 194815
[UPDATE], 99thPercentileLatency(us), 601087
[UPDATE], Return=OK, 1052981
  • 总体的,Throughput 为 2784/s 。
  • 读操作,平均延迟为 35419us = 35.419ms 。
  • 写操作,平均延迟为 35384us = 35.384ms 。

4.7.4.2 ESSD 云盘

mongo client connection created with mongodb://172.19.83.156:27017/ycsb
[OVERALL], RunTime(ms), 683477
[OVERALL], Throughput(ops/sec), 2926.2140496315164
[TOTAL_GCS_PS_Scavenge], Count, 1277
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 2422
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.35436452141037666
[TOTAL_GCS_PS_MarkSweep], Count, 1
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 26
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0038040782645209718
[TOTAL_GCs], Count, 1278
[TOTAL_GC_TIME], Time(ms), 2448
[TOTAL_GC_TIME_%], Time(%), 0.3581685996748976
[READ], Operations, 947236
[READ], AverageLatency(us), 33152.569454708224
[READ], MinLatency(us), 170
[READ], MaxLatency(us), 6815743
[READ], 95thPercentileLatency(us), 190335
[READ], 99thPercentileLatency(us), 619519
[READ], Return=OK, 947236
[CLEANUP], Operations, 100
[CLEANUP], AverageLatency(us), 1955.1
[CLEANUP], MinLatency(us), 1
[CLEANUP], MaxLatency(us), 195327
[CLEANUP], 95thPercentileLatency(us), 4
[CLEANUP], 99thPercentileLatency(us), 26
[UPDATE], Operations, 1052764
[UPDATE], AverageLatency(us), 33992.01787865086
[UPDATE], MinLatency(us), 183
[UPDATE], MaxLatency(us), 6803455
[UPDATE], 95thPercentileLatency(us), 191359
[UPDATE], 99thPercentileLatency(us), 687103
[UPDATE], Return=OK, 1052764
  • 总体的,Throughput 为 2926/s 。
  • 读操作,平均延迟为 33152us = 33.152ms 。
  • 写操作,平均延迟为 33992us = 33.992ms 。

4.7.4.3 对比总结

好像,打脸了,扎心。看了下主机监控,无论是 SSD、还是 ESSD 的主机,IOPS 的峰值都在 2500/s 左右,读写吞吐量峰值在 170 MB/s ,而且常常不在峰值。。。虽然说性能上有一定的提升,但是应该是 CPU 核数的提升,带来的并发处理性能所带来的。这也是为什么第四轮测试 IOPS 的峰值在 1700/s 左右,而本轮在 2500/s 左右。

重新去查看了下 MongoDB 服务的日志,发现有大量的锁竞争,这个应该会是一个影响因素。然后,去查看了下 ESSD 和 SSD 云盘的说法,它们的单路随机写访问时延如下:

  • ESSD 云盘:0.1−0.2 ms
  • SSD 云盘:0.5−2 ms

这意味着啥呢?艿艿之前关注 IOPS 的点不正确,不应该去关注 IOPS 达到多少峰值,而是更低的延迟,带来的吞吐量的提升。所以,咱们进入第五轮测试,降低并发量。

4.7.5 第五轮

配置上,我们还是继续沿用【第四轮】的,唯一的调整,就是修改 YCSB 的并发数,改成 1 ,完全无并发。如下:

bin/ycsb run mongodb -s -P workloads/workloada -threads 1 > outputRun.txt

4.7.5.1 SSD 云盘

mongo client connection created with mongodb://172.19.83.160:27017/ycsb
[OVERALL], RunTime(ms), 538421
[OVERALL], Throughput(ops/sec), 3714.5653679927045
[TOTAL_GCS_PS_Scavenge], Count, 1855
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 1819
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.3378397202189365
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 1855
[TOTAL_GC_TIME], Time(ms), 1819
[TOTAL_GC_TIME_%], Time(%), 0.3378397202189365
[READ], Operations, 1800372
[READ], AverageLatency(us), 265.1663661732131
[READ], MinLatency(us), 149
[READ], MaxLatency(us), 600063
[READ], 95thPercentileLatency(us), 270
[READ], 99thPercentileLatency(us), 1512
[READ], Return=OK, 1800372
[CLEANUP], Operations, 1
[CLEANUP], AverageLatency(us), 2063.0
[CLEANUP], MinLatency(us), 2062
[CLEANUP], MaxLatency(us), 2063
[CLEANUP], 95thPercentileLatency(us), 2063
[CLEANUP], 99thPercentileLatency(us), 2063
[UPDATE], Operations, 199628
[UPDATE], AverageLatency(us), 288.63570240647607
[UPDATE], MinLatency(us), 169
[UPDATE], MaxLatency(us), 80511
[UPDATE], 95thPercentileLatency(us), 300
[UPDATE], 99thPercentileLatency(us), 1511
[UPDATE], Return=OK, 199628
  • 总体的,Throughput 为 3714/s 。
  • 读操作,平均延迟为 265us = 0.265ms 。
  • 写操作,平均延迟为 288us = 0.288ms 。

4.7.5.2 ESSD 云盘

mongo client connection created with mongodb://172.19.83.159:27017/ycsb
[OVERALL], RunTime(ms), 480221
[OVERALL], Throughput(ops/sec), 4164.749146747018
[TOTAL_GCS_PS_Scavenge], Count, 1847
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 1765
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.3675391122004244
[TOTAL_GCS_PS_MarkSweep], Count, 0
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 0
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0
[TOTAL_GCs], Count, 1847
[TOTAL_GC_TIME], Time(ms), 1765
[TOTAL_GC_TIME_%], Time(%), 0.3675391122004244
[READ], Operations, 1800507
[READ], AverageLatency(us), 235.88793434293785
[READ], MinLatency(us), 144
[READ], MaxLatency(us), 100223
[READ], 95thPercentileLatency(us), 277
[READ], 99thPercentileLatency(us), 787
[READ], Return=OK, 1800507
[CLEANUP], Operations, 1
[CLEANUP], AverageLatency(us), 10756.0
[CLEANUP], MinLatency(us), 10752
[CLEANUP], MaxLatency(us), 10759
[CLEANUP], 95thPercentileLatency(us), 10759
[CLEANUP], 99thPercentileLatency(us), 10759
[UPDATE], Operations, 199493
[UPDATE], AverageLatency(us), 261.10529191500456
[UPDATE], MinLatency(us), 168
[UPDATE], MaxLatency(us), 96319
[UPDATE], 95thPercentileLatency(us), 308
[UPDATE], 99thPercentileLatency(us), 808
[UPDATE], Return=OK, 199493
  • 总体的,Throughput 为 4164/s 。
  • 读操作,平均延迟为 235us = 0.235ms 。
  • 写操作,平均延迟为 261us = 0.261ms 。

4.7.5.3 对比总结

在本轮测试中,多次反复跑 YCSB 测试,ESSD 终于相对稳定的比 SSD 云盘的情况下高。

心累~当然,目前 ESSD 云盘还在公测中,等后续正式使用,在找时间跑几次。

4.8 推荐文章

不过瘾的胖友,可以继续撸一撸下面的文章:

666. 彩蛋

暂时木有彩蛋,自闭去了。

写的有一些流水账,基本完整的记录了整个测试的过程。

后来又拿了 12C 的阿里云服务器,依然跑不高 IOPS 。目前的想法,想要跑满 IOPS 是比较困难的事情。如果有办法的胖友,麻烦分享下,谢谢。

对了,如果真的做 MongoDB 基准性能测试,还是按照【第一轮】的方式,毕竟日常使用,热数据肯定是在内存为主的。

😈 欢迎胖友交流起来。

对了,因为本文并未比较 MySQL 与 MongoDB 的性能差别,在这里先推荐几篇对比的文章,艿艿觉得比较靠谱的:

文章目录
  1. 1. 1. 概述
  2. 2. 2. 性能指标
  3. 3. 3. 测试工具
  4. 4. 4. YCSB
    1. 4.1. 4.1 测试环境
    2. 4.2. 4.2 安装 MongoDB
    3. 4.3. 4.3 安装 JDK
    4. 4.4. 4.4 安装 Maven
    5. 4.5. 4.5 安装 YCSB
    6. 4.6. 4.6 使用指南
      1. 4.6.1. 4.6.1 load 阶段
      2. 4.6.2. 4.6.2 run 阶段
      3. 4.6.3. 4.6.3 workload 模板
    7. 4.7. 4.7 测试结果
      1. 4.7.1. 4.7.1 第一轮
        1. 4.7.1.1. 4.7.1.1 SSD 云盘
        2. 4.7.1.2. 4.7.1.2 ESSD 云盘
        3. 4.7.1.3. 4.7.1.3 对比总结
      2. 4.7.2. 4.7.2 第二轮
        1. 4.7.2.1. 4.7.2.1 SSD 云盘
        2. 4.7.2.2. 4.7.2.2 ESSD 云盘
        3. 4.7.2.3. 4.7.2.3 对比总结
      3. 4.7.3. 4.7.3 第三轮
        1. 4.7.3.1. 4.7.3.1 SSD 云盘
        2. 4.7.3.2. 4.7.3.2 ESSD 云盘
        3. 4.7.3.3. 4.7.3.3 对比总结
      4. 4.7.4. 4.7.4 第四轮
        1. 4.7.4.1. 4.7.4.1 SSD 云盘
        2. 4.7.4.2. 4.7.4.2 ESSD 云盘
        3. 4.7.4.3. 4.7.4.3 对比总结
      5. 4.7.5. 4.7.5 第五轮
        1. 4.7.5.1. 4.7.5.1 SSD 云盘
        2. 4.7.5.2. 4.7.5.2 ESSD 云盘
        3. 4.7.5.3. 4.7.5.3 对比总结
    8. 4.8. 4.8 推荐文章
  5. 5. 666. 彩蛋