Nodejs編寫的應(yīng)用,后端連MongoDB進(jìn)行持久化存儲(chǔ)表制。Mongodb分別有兩個(gè)版本撩炊,分別跑在虛擬機(jī)和kubernetes里面放仗,進(jìn)行讀寫性能測(cè)試
測(cè)試基線
分別在虛擬機(jī)和K8S中安裝Mongodb數(shù)據(jù)庫,版本為4.0.18包警,測(cè)試目標(biāo)為考察mongodb跑在虛擬機(jī)和K8S中的性能差異
- 虛擬機(jī)配置:2C4G
- K8S中的pod配額(limits)為2C4G撵摆,mongodb后端掛接ceph rbd存儲(chǔ)
壓力測(cè)試使用的軟件為wrk,地址為:https://github.com/wg/wrk害晦。這里推薦一下wrk特铝,可以以極少的線程模擬出高并發(fā)的場(chǎng)景,比之前用的ab好用了不是一點(diǎn)壹瘟。
Node性能測(cè)試
首先我們來看下node的基礎(chǔ)性能測(cè)試苟呐,眾所周知nodejs是異步非阻塞的編程模型,在處理高并發(fā)場(chǎng)景有天然優(yōu)勢(shì)俐筋,這邊我們測(cè)試一下,在不接后端數(shù)據(jù)庫的情況下严衬,純nodejs處理(比如請(qǐng)求校驗(yàn)錯(cuò)誤)澄者,性能可以到多少。node應(yīng)用也是跑在k8里的请琳,設(shè)置的配額(limits)為6C粱挡,并發(fā)數(shù)設(shè)置為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多,而且這個(gè)數(shù)值是線性增長的俄精,即如果起兩個(gè)實(shí)例询筏,則QPS可以到6000多,3個(gè)實(shí)例的話可以到9000多竖慧。記住這個(gè)數(shù)據(jù)嫌套,這是后面進(jìn)行比較的基礎(chǔ)(單個(gè)node的最高性能,也就是加上數(shù)據(jù)庫以后理論上也不會(huì)超過這個(gè)QPS)
虛擬機(jī)Mongo實(shí)例測(cè)試
node后端接入跑在虛擬機(jī)上的mongodb實(shí)例(mongo本身均為單實(shí)例圾旨,未做集群)踱讨,我們來看一下:
寫入測(cè)試
單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個(gè)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個(gè)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%
寫入測(cè)試總結(jié)
總體來說隨著副本數(shù)增加,整體性能呈線性增加砍的,5副本時(shí)mongo數(shù)據(jù)庫的cpu也只有60%痹筛,應(yīng)該說還有余量。應(yīng)該是mongodb默認(rèn)的連接池的限制起了作用(默認(rèn)連接數(shù)為5個(gè))
讀取測(cè)試
數(shù)據(jù)庫中放置測(cè)試數(shù)據(jù)780萬條廓鞠,根據(jù)ObjectId關(guān)鍵字進(jìn)行查詢:
單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個(gè)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%
讀取測(cè)試總結(jié)
讀取方面帚稠,因?yàn)橛芯彺娴年P(guān)系(命中率較高),整體的QPS比寫入還略高床佳,基本也是隨副本數(shù)呈線性增長的態(tài)勢(shì)滋早。
kubernetes mongo 實(shí)例測(cè)試
接下來我們測(cè)試一下,mongodb跑在k8s中夕土,后端掛ceph塊存儲(chǔ)的性能(k8的work與ceph集群連接為千兆網(wǎng)卡)
寫入測(cè)試
單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個(gè)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個(gè)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%
寫入測(cè)試總結(jié)
可以看到使用網(wǎng)絡(luò)存儲(chǔ)和本地磁盤馆衔,在寫入性能上基本沒有區(qū)別瘟判,在單副本情況下,使用rbd的性能甚至還好于本地磁盤角溃。
讀取測(cè)試
單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個(gè)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%
測(cè)試總結(jié)
可以看到拷获,不管是跑在虛擬機(jī)中,還是k8s里减细,測(cè)試的結(jié)果是基本接近的匆瓜。而按照我們的一般認(rèn)知,本地磁盤的讀寫性能肯定還是要高于網(wǎng)絡(luò)存儲(chǔ)的未蝌,這里面的關(guān)鍵驮吱,還是要說到mongodb的讀寫機(jī)制。mongo作為一款典型的nosql數(shù)據(jù)庫萧吠,其絕大部分的操作都是在內(nèi)存中完成的左冬。MongoDB把文檔寫進(jìn)內(nèi)存之后就返回了,所以說QPS基本不受后端存儲(chǔ)性能的影響纸型。
這邊簡單說一下mongodb數(shù)據(jù)存儲(chǔ)的原理:
內(nèi)存映射機(jī)制
Mongo使用了內(nèi)存映射技術(shù)(mmap) - 寫入數(shù)據(jù)時(shí)候只要在內(nèi)存里完成就可以返回給應(yīng)用程序拇砰,而保存到硬盤的操作則在后臺(tái)異步完成。這也就是說狰腌,MongoDB并不對(duì)RAM和磁盤這兩者進(jìn)行區(qū)別對(duì)待除破,只是將文件看作一個(gè)巨大的數(shù)組,然后按照字節(jié)為單位訪問其中的數(shù)據(jù)琼腔,剩下的都交由操作系統(tǒng)(OS)去處理瑰枫。先了解一下Memeory-Mapped Files:
1、內(nèi)存映射文件是OS通過mmap在內(nèi)存中創(chuàng)建一個(gè)數(shù)據(jù)文件丹莲,這樣就把文件映射到一個(gè)虛擬內(nèi)存的區(qū)域光坝。
2、虛擬內(nèi)存對(duì)于進(jìn)程來說甥材,是一個(gè)物理內(nèi)存的抽象教馆,尋址空間大小為2^64
3、操作系統(tǒng)通過mmap來把進(jìn)程所需的所有數(shù)據(jù)映射到這個(gè)地址空間(紅線)擂达,然后再把當(dāng)前需要處理的數(shù)據(jù)映射到物理內(nèi)存(灰線)
4土铺、當(dāng)進(jìn)程訪問某個(gè)數(shù)據(jù)時(shí),如果數(shù)據(jù)不在虛擬內(nèi)存里板鬓,觸發(fā)page fault悲敷,然后OS從硬盤里把數(shù)據(jù)加載進(jìn)虛擬內(nèi)存和物理內(nèi)存
5、如果物理內(nèi)存滿了俭令,觸發(fā)swap-out操作后德,這時(shí)有些數(shù)據(jù)就需要寫回磁盤,如果是純粹的內(nèi)存數(shù)據(jù)抄腔,寫回swap分區(qū)瓢湃,如果不是就寫回磁盤理张。
Storage View
Mongodb有三個(gè)storage view:Share view,private view绵患,journal日志雾叭,前兩個(gè)位于內(nèi)存中,后一個(gè)位于磁盤上落蝙。
- Share view:位于內(nèi)存上织狐,會(huì)存儲(chǔ)已經(jīng)改變的要刷新到磁盤上的數(shù)據(jù)(臟數(shù)據(jù))。Share view是唯一一個(gè)直接連映射到數(shù)據(jù)庫文件上的view筏勒。當(dāng)你啟用mongoDB的日志功能時(shí)移迫,mongod會(huì)請(qǐng)求操作系統(tǒng)把磁盤上的數(shù)據(jù)文件指向share view內(nèi)存視圖上。操作系統(tǒng)不會(huì)數(shù)據(jù)文件加載到share view中管行。Mongdb在需要時(shí)自己把數(shù)據(jù)文件加載到share view上厨埋。
- Private view:位于內(nèi)存上,存儲(chǔ)用于讀請(qǐng)求的數(shù)據(jù)捐顷,更改請(qǐng)求最先在這執(zhí)行揽咕。MongDB把Private view指向share view。
- Journal view:存儲(chǔ)已經(jīng)在private cache上發(fā)生更改的數(shù)據(jù)套菜,但是會(huì)在更改數(shù)據(jù)刷新到 share view(cache) 之前存儲(chǔ)。Journal 確保了數(shù)據(jù)的持久化设易。如果更改的數(shù)據(jù)沒有刷新到磁盤上的數(shù)據(jù)文件里逗柴,mongodb crash了,當(dāng)mongodb 起來以后顿肺,mongodb會(huì)把journallog中沒應(yīng)用到數(shù)據(jù)文件中的數(shù)據(jù)回放到 share view(cache) 中戏溺,最終會(huì)應(yīng)用到數(shù)據(jù)文件中。
當(dāng)一個(gè)寫請(qǐng)求發(fā)生時(shí):
1屠尊、更改 private view (cache)中的數(shù)據(jù)
2旷祸、默認(rèn)每100毫秒刷新到j(luò)ournal log。journal log有一個(gè)記錄當(dāng)前日志點(diǎn)的pointer
3讼昆、應(yīng)用journal log中的寫操作到share view ,這時(shí)share view 就和數(shù)據(jù)文件不一致
4托享、默認(rèn)每隔60秒,mongodb會(huì)請(qǐng)求操作系統(tǒng)刷新shared view中更改的數(shù)據(jù)到數(shù)據(jù)文件
5浸赫、mongdb會(huì)把journal log中記錄更改數(shù)據(jù)日志點(diǎn)的pointer闰围,以前的數(shù)據(jù)刪除掉。
6既峡、為了數(shù)據(jù)的一致性羡榴,Mongodb通常會(huì)請(qǐng)求操作系統(tǒng)重新把share view 指向private view。
所以說运敢,在客戶端連接數(shù)一定的情況下(使用mongoose情況下連接池默認(rèn)為5)校仑,單個(gè)node實(shí)例占用的mongodb資源是固定的忠售,性能也是接近的(都是操作內(nèi)存),實(shí)測(cè)將k8s中的mongodb實(shí)例的內(nèi)存配額調(diào)低到2GB迄沫,測(cè)出的性能沒有太大差距稻扬,所以說mongo的性能和內(nèi)存的關(guān)系不大(當(dāng)然大內(nèi)存會(huì)降低cpu的資源消耗)。
補(bǔ)充測(cè)試
既然前面提高了使用node連接mongo邢滑,默認(rèn)連接池大小為5(數(shù)據(jù)庫連接相關(guān)設(shè)置腐螟,請(qǐng)參考http://www.mongoosejs.net/docs/connections.html
),那么我們來測(cè)試一下困后,如果調(diào)整參數(shù)乐纸,是否會(huì)對(duì)于性能產(chǎn)生影響。仍然還是單node副本摇予,數(shù)據(jù)庫連接配置如下:
config.mongoose = {
client: {
url: 'mongodb://22.196.66.200:31669/test',
options: {
user: 'test',
pass: 'test',
poolSize: 50,
},
},
};
調(diào)整為50汽绢,測(cè)試結(jié)果如下:
[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%
結(jié)果:增強(qiáng)pooSize大小對(duì)于QPS并沒有實(shí)質(zhì)性影響,最有效的增加性能的方式還是添加實(shí)例數(shù)侧戴。從測(cè)試結(jié)果來看宁昭,影響QPS的不是mongodb的連接數(shù),而是nodejs的并發(fā)數(shù)限制酗宋,因?yàn)閚ode本身是單進(jìn)程模型积仗,單進(jìn)程能處理的并發(fā)數(shù)有限,不能充分利用cpu多核的優(yōu)勢(shì)蜕猫。
為了進(jìn)一步驗(yàn)證寂曹,我們把eggjs的工作線程workers的數(shù)量設(shè)置為4,重啟pod進(jìn)行測(cè)試:
[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性能的主要因素還是**線程數(shù)**
參考資料:
[http://blog.chinaunix.net/uid-25135004-id-3810200.html](http://blog.chinaunix.net/uid-25135004-id-3810200.html)