MongoDB性能测试

2020-05-25  本文已影响0人  暴走的初号机

Nodejs编写的应用,后端连MongoDB进行持久化存储。Mongodb分别有两个版本,分别跑在虚拟机和kubernetes里面,进行读写性能测试

测试基线

分别在虚拟机和K8S中安装Mongodb数据库,版本为4.0.18,测试目标为考察mongodb跑在虚拟机和K8S中的性能差异

压力测试使用的软件为wrk,地址为:https://github.com/wg/wrk。这里推荐一下wrk,可以以极少的线程模拟出高并发的场景,比之前用的ab好用了不是一点。

Node性能测试

首先我们来看下node的基础性能测试,众所周知nodejs是异步非阻塞的编程模型,在处理高并发场景有天然优势,这边我们测试一下,在不接后端数据库的情况下,纯nodejs处理(比如请求校验错误),性能可以到多少。node应用也是跑在k8里的,设置的配额(limits)为6C,并发数设置为200:

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s -s demo.lua http://22.196.66.200:31010/users
Running 10s test @ http://22.196.66.200:31010/users
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    61.84ms   26.29ms 479.18ms   94.73%
    Req/Sec     1.65k   209.52     2.03k    85.00%
  32905 requests in 10.01s, 8.38MB read
  Non-2xx or 3xx responses: 32905
Requests/sec:   3287.93
Transfer/sec:    857.30KB

可以看到QPS在3200多,而且这个数值是线性增长的,即如果起两个实例,则QPS可以到6000多,3个实例的话可以到9000多。记住这个数据,这是后面进行比较的基础(单个node的最高性能,也就是加上数据库以后理论上也不会超过这个QPS)

虚拟机Mongo实例测试

node后端接入跑在虚拟机上的mongodb实例(mongo本身均为单实例,未做集群),我们来看一下:

写入测试

单node副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s -s demo.lua http://22.196.66.200:31010/users
Running 10s test @ http://22.196.66.200:31010/users
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   810.15ms    1.62s    9.80s    88.46%
    Req/Sec   317.73    113.81   727.00     71.36%
  6323 requests in 10.02s, 3.41MB read
Requests/sec:    631.35
Transfer/sec:    348.95KB

cpu使用率为20%

2个node副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s -s demo.lua -d 30s http://22.196.66.200:31010/users
Running 30s test @ http://22.196.66.200:31010/users
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   176.34ms  266.36ms   3.79s    97.12%
    Req/Sec   710.32    168.50     1.01k    63.83%
  42445 requests in 30.03s, 22.91MB read
Requests/sec:   1413.36
Transfer/sec:    781.11KB

cpu使用率为40%

5个node副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s -s demo.lua -d 30s http://22.196.66.200:31010/users
Running 30s test @ http://22.196.66.200:31010/users
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    60.30ms   35.27ms   1.06s    96.79%
    Req/Sec     1.71k   236.41     2.18k    76.83%
  102166 requests in 30.02s, 55.12MB read
Requests/sec:   3403.58
Transfer/sec:      1.84MB

cpu使用率为60%

写入测试总结

总体来说随着副本数增加,整体性能呈线性增加,5副本时mongo数据库的cpu也只有60%,应该说还有余量。应该是mongodb默认的连接池的限制起了作用(默认连接数为5个)

读取测试

数据库中放置测试数据780万条,根据ObjectId关键字进行查询:

单node副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s http://22.196.66.200:31010/users/5ecb83b3c5299b002bf8bd09
Running 10s test @ http://22.196.66.200:31010/users/5ecb83b3c5299b002bf8bd09
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   726.33ms    1.46s    8.79s    88.23%
    Req/Sec   524.42    158.59     1.00k    72.00%
  10448 requests in 10.01s, 5.59MB read
Requests/sec:   1043.39
Transfer/sec:    572.14KB

cpu利用率16%

5个node副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s -d 30s http://22.196.66.200:31010/users/5ecb83b3c5299b002bf8bd09
Running 30s test @ http://22.196.66.200:31010/users/5ecb83b3c5299b002bf8bd09
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    33.17ms   18.24ms 584.77ms   87.45%
    Req/Sec     3.10k   303.76     3.64k    85.17%
  184886 requests in 30.01s, 98.91MB read
Requests/sec:   6159.80
Transfer/sec:      3.30MB

cpu利用率63%

读取测试总结

读取方面,因为有缓存的关系(命中率较高),整体的QPS比写入还略高,基本也是随副本数呈线性增长的态势。

kubernetes mongo 实例测试

接下来我们测试一下,mongodb跑在k8s中,后端挂ceph块存储的性能(k8的work与ceph集群连接为千兆网卡)

写入测试

单node副本

[root@dce304-cal-vm3 ~]# wrk -c 500 -s demo.lua -T 30s http://22.196.66.200:31010/users
Running 10s test @ http://22.196.66.200:31010/users
  2 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   470.51ms    1.13s    9.77s    93.67%
    Req/Sec   398.62    150.61     0.91k    71.81%
  7654 requests in 10.02s, 4.13MB read
Requests/sec:    763.54
Transfer/sec:    422.03KB

cpu使用率 13.7%

2个node副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -s demo.lua -T 30s http://22.196.66.200:31010/users
Running 10s test @ http://22.196.66.200:31010/users
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   281.70ms  628.13ms   5.50s    93.31%
    Req/Sec   738.18    142.56     1.14k    79.50%
  14710 requests in 10.01s, 7.94MB read
Requests/sec:   1468.87
Transfer/sec:    811.73KB

cpu使用率25%

5个node 副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -s demo.lua -T 30s http://22.196.66.200:31010/users
Running 10s test @ http://22.196.66.200:31010/users
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    61.27ms   29.67ms 453.58ms   83.79%
    Req/Sec     1.66k   233.53     2.09k    93.00%
  33091 requests in 10.02s, 17.85MB read
Requests/sec:   3303.79
Transfer/sec:      1.78MB

cpu使用率57%

写入测试总结

可以看到使用网络存储和本地磁盘,在写入性能上基本没有区别,在单副本情况下,使用rbd的性能甚至还好于本地磁盘。

读取测试

单node 副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s http://22.196.66.200:31010/users/5ecba669f41c9b002b91cfbc
Running 10s test @ http://22.196.66.200:31010/users/5ecba669f41c9b002b91cfbc
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   564.36ms    1.19s    7.58s    89.28%
    Req/Sec   610.83    224.26     1.01k    58.38%
  12119 requests in 10.02s, 6.49MB read
Requests/sec:   1210.00
Transfer/sec:    663.37KB

cpu使用率20%

5个node副本

[root@dce304-cal-vm3 ~]# wrk -c 200 -T 30s http://22.196.66.200:31010/users/5ecba669f41c9b002b91cfbc
Running 10s test @ http://22.196.66.200:31010/users/5ecba669f41c9b002b91cfbc
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    36.44ms   26.26ms 542.40ms   96.53%
    Req/Sec     2.91k   498.88     3.58k    87.00%
  58009 requests in 10.01s, 31.03MB read
Requests/sec:   5792.89
Transfer/sec:      3.10MB

cpu使用率91%

测试总结

可以看到,不管是跑在虚拟机中,还是k8s里,测试的结果是基本接近的。而按照我们的一般认知,本地磁盘的读写性能肯定还是要高于网络存储的,这里面的关键,还是要说到mongodb的读写机制。mongo作为一款典型的nosql数据库,其绝大部分的操作都是在内存中完成的。MongoDB把文档写进内存之后就返回了,所以说QPS基本不受后端存储性能的影响。

这边简单说一下mongodb数据存储的原理:

内存映射机制

Mongo使用了内存映射技术(mmap) - 写入数据时候只要在内存里完成就可以返回给应用程序,而保存到硬盘的操作则在后台异步完成。这也就是说,MongoDB并不对RAM和磁盘这两者进行区别对待,只是将文件看作一个巨大的数组,然后按照字节为单位访问其中的数据,剩下的都交由操作系统(OS)去处理。先了解一下Memeory-Mapped Files:
1、内存映射文件是OS通过mmap在内存中创建一个数据文件,这样就把文件映射到一个虚拟内存的区域。
2、虚拟内存对于进程来说,是一个物理内存的抽象,寻址空间大小为2^64
3、操作系统通过mmap来把进程所需的所有数据映射到这个地址空间(红线),然后再把当前需要处理的数据映射到物理内存(灰线)
4、当进程访问某个数据时,如果数据不在虚拟内存里,触发page fault,然后OS从硬盘里把数据加载进虚拟内存和物理内存
5、如果物理内存满了,触发swap-out操作,这时有些数据就需要写回磁盘,如果是纯粹的内存数据,写回swap分区,如果不是就写回磁盘。


mmap.png

Storage View

Mongodb有三个storage view:Share view,private view,journal日志,前两个位于内存中,后一个位于磁盘上。

当一个写请求发生时:
1、更改 private view (cache)中的数据
2、默认每100毫秒刷新到journal log。journal log有一个记录当前日志点的pointer
3、应用journal log中的写操作到share view ,这时share view 就和数据文件不一致
4、默认每隔60秒,mongodb会请求操作系统刷新shared view中更改的数据到数据文件
5、mongdb会把journal log中记录更改数据日志点的pointer,以前的数据删除掉。
6、为了数据的一致性,Mongodb通常会请求操作系统重新把share view 指向private view。


image.png

所以说,在客户端连接数一定的情况下(使用mongoose情况下连接池默认为5),单个node实例占用的mongodb资源是固定的,性能也是接近的(都是操作内存),实测将k8s中的mongodb实例的内存配额调低到2GB,测出的性能没有太大差距,所以说mongo的性能和内存的关系不大(当然大内存会降低cpu的资源消耗)。

补充测试

既然前面提高了使用node连接mongo,默认连接池大小为5(数据库连接相关设置,请参考http://www.mongoosejs.net/docs/connections.html
),那么我们来测试一下,如果调整参数,是否会对于性能产生影响。仍然还是单node副本,数据库连接配置如下:

  config.mongoose = {
    client: {
      url: 'mongodb://22.196.66.200:31669/test',
      options: {
        user: 'test',
        pass: 'test',
        poolSize: 50,
      },
    },
  };

调整为50,测试结果如下:

[root@dce304-cal-vm3 ~]# wrk -c 200 -s demo.lua http://22.196.66.200:31010/users
Running 10s test @ http://22.196.66.200:31010/users
  2 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   188.41ms  111.62ms   1.87s    90.22%
    Req/Sec   316.79    126.13   626.00     70.90%
  6236 requests in 10.02s, 3.37MB read
  Socket errors: connect 0, read 0, write 0, timeout 85
Requests/sec:    622.50
Transfer/sec:    344.11KB

cpu使用率为13%
结果:增强pooSize大小对于QPS并没有实质性影响,最有效的增加性能的方式还是添加实例数。从测试结果来看,影响QPS的不是mongodb的连接数,而是nodejs的并发数限制,因为node本身是单进程模型,单进程能处理的并发数有限,不能充分利用cpu多核的优势。

为了进一步验证,我们把eggjs的工作线程workers的数量设置为4,重启pod进行测试:
[root@dce304-cal-vm3 ~]# wrk -c 200 -s demo.lua http://22.196.66.200:31010/users
Running 10s test @ http://22.196.66.200:31010/users
2 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 82.35ms 29.02ms 347.47ms 76.04%
Req/Sec 1.21k 285.20 1.76k 81.50%
24194 requests in 10.02s, 13.06MB read
Requests/sec: 2415.47
Transfer/sec: 1.30MB

可以看出来QPS马上变成原来的4倍,所以说影响nodejs性能的主要因素还是**线程数**

参考资料:
[http://blog.chinaunix.net/uid-25135004-id-3810200.html](http://blog.chinaunix.net/uid-25135004-id-3810200.html)

上一篇 下一篇

猜你喜欢

热点阅读