NodeJS性能調(diào)優(yōu)之GC調(diào)優(yōu)

背景

近期孽鸡,為了評估服務(wù)性能搬男,測試同學(xué)對關(guān)鍵業(yè)務(wù)接口進行了壓測拣展,單臺NodeJS服務(wù)開啟3個進程的情況下,QPS最高達320多缔逛。為了確認服務(wù)是否還有優(yōu)化空間,我們使用阿里云的 NodeJS性能平臺 對服務(wù)進行分析,定位了服務(wù)的瓶頸褐奴,并在阿里云的同學(xué)幫助下采取了相應(yīng)的措施按脚,優(yōu)化了服務(wù)的性能。

問題排查與分析

Step1 獲取與分析CPU Profile

當我們以400并發(fā)量敦冬,對單一業(yè)務(wù)接口進行壓測辅搬,發(fā)現(xiàn)QPS為320時,服務(wù)器CPU被打滿脖旱。為了找到是什么原因?qū)е翪PU達到了性能瓶頸堪遂,我們使用了阿里云的「NodeJS性能平臺」,抓取了壓測時的 CPU Profile 信息萌庆。

CPU Profile

經(jīng)過分析溶褪,我們發(fā)現(xiàn) _tickDomainCallbackgarbage collector 在CPU占比很大,其中 _tickDomainCallback占了50%多践险,GC 也占了27%的比例猿妈。通過展開 _tickDomainCallback 里的內(nèi)容,發(fā)現(xiàn)CPU占比高的邏輯主要是TypeORM 和4處業(yè)務(wù)邏輯巍虫。

_tickDomainCallback 展開信息

Step2 排查數(shù)據(jù)庫性能

當我們看到TypeORM時彭则,我們以為是數(shù)據(jù)庫消費不過來(生產(chǎn)與消費能力不匹配,Query隊列產(chǎn)生大量堆積)占遥,導(dǎo)致TypeORM消耗大量CPU資源俯抖。后來,我們進行了第二次壓測瓦胎,并在服務(wù)器CPU打滿時獲取了RDS的性能分析報告芬萍。報告顯示:

  • 數(shù)據(jù)庫CPU使用了15%的資源
  • 平均查詢響應(yīng)速度小于15ms
  • 無慢查詢記錄
  • 無死鎖記錄

因此,我們排除了RDS導(dǎo)致TypeORM消耗CPU資源凛捏。我們推測可能與TypeORM本身的代碼有關(guān)担忧,我們使用了一個非常早期的TypeORM版本(v0.0.11)。阿里云的同學(xué)推薦我們升級TypeORM的版本試試坯癣,看看會不會有所改善瓶盛。但是最新的TypeORM版本與早期的版本API已經(jīng)發(fā)生了變化,無法進行平滑升級示罗。因此惩猫,放棄了對TypeORM優(yōu)化。

Step3 排查業(yè)務(wù)邏輯代碼

我們將可能影響性能的業(yè)務(wù)代碼進行了Review蚜点,發(fā)現(xiàn)優(yōu)化空間并不是很大轧房,代碼本身已經(jīng)經(jīng)過了精簡和優(yōu)化。無法進行進一步提升绍绘,我們將優(yōu)化重點放在了占比高達27%的 GC 上奶镶。

Step4 GC 信息抓取與分析

為了獲得詳細的GC信息迟赃,我們再次進行了壓測,并獲取了 GC Trace 信息厂镇。結(jié)果如下圖:

GC Trace

從圖中纤壁,我們可以獲取到一些重要信息:

  • GC時間占比為26.87%
  • 3分鐘內(nèi),GC暫時時間為47.8s捺信,且scavenge占了大多數(shù)
  • 平均GC暫停時間為50~60ms

根據(jù)這些信息酌媒,我們可以得出 scavenge 非常頻繁,導(dǎo)致了CPU資源的占用迄靠。
scavenge 發(fā)生在新生代的內(nèi)存回收階段秒咨,這個階段觸發(fā)條件是, semi space allocation failed(半空間分配失敗)掌挚∮晗可以推測出,壓測期間我們的代碼邏輯頻繁的生成大量的小對象疫诽,導(dǎo)致 semi space很快被分配滿舅世,從而導(dǎo)致了 scavenge 回收和CPU資源的占用。既然這樣奇徒,我們可不可通過調(diào)整 semi space(半空間)的大小雏亚,減少GC的次數(shù)來優(yōu)化對CPU的占用。

Step5 GC 調(diào)優(yōu)與測試

NodeJS在64位系統(tǒng)上摩钙,默認的semi space大小為16M罢低。

我們將 semi space 進行了3次調(diào)整,分別設(shè)為64M胖笛、128M网持、256M,對不同值情況下的服務(wù)進行了壓測并獲取了對應(yīng) GC TraceCPU Profile长踊。

修改 semi space 方法

對于普通node服務(wù):

node index.js --max_semi_space_size=64

對于PM2啟動的服務(wù)功舀,在pm2的config文件中添加:

node_args: '--max_semi_space_size=64',

1) 64M

semi space 修改為64M,并進行線上壓測身弊,獲取壓測時的 GC TraceCPU Profile信息:

GC Profile信息

CPU Profile

對比修改前的數(shù)據(jù)辟汰,我們發(fā)現(xiàn):

  • GC的CPU占比從27.5%下降到了7.14%;
  • 3分鐘內(nèi)GC次數(shù)阱佛,從1008次降到了312次帖汞。其中,Scavenge的次數(shù)從988次下降到了294次凑术;
  • GC時間翩蘸,從原來的47.7s下降到了11.8s
  • GC平均暫停時間在40ms左右

GC時間從47.7s下降到了11.8s,相應(yīng)的淮逊,QPS提升了10%催首。

2) 128M

semi space 調(diào)整到128M扶踊,得到的 GC TraceCPU Profile信息:

GC Trace

CPU Profile

對比64M時的數(shù)據(jù),我們可以發(fā)現(xiàn):

  • 與64M時GC次相比翅帜,GC次數(shù)從312下降到了145姻檀;
  • Scavenge算法回收時間命满,增加了1倍涝滴。從平均50ms漲到了100ms;
  • Mark-sweep的次數(shù)沒有發(fā)生變化
  • CPU占比略微下降胶台,從7.14降到了6.71

可以看出歼疮,將 semi space從64M調(diào)整到了128M,性能并沒有很大的提升诈唬。相反韩脏,Scavenge算法回收時間幾乎增長了一倍。

3) 256M

semi space 調(diào)整到256M铸磅,得到的 GC TraceCPU Profile信息:

GC Trace

CPU Profile

可以觀察到:

  • 與128M時相比赡矢,GC次數(shù)下降了一倍
  • 但是Scavenge回收的時間,波動到了150ms阅仔。
  • CPU占比吹散,也略微下降了一點,降到了5.99

可以看出八酒,將 semi space調(diào)整到了 256M空民,性能并沒有顯著提升,且增加了 Scavenge 的回收時間羞迷。

小結(jié)

semi space 從16M調(diào)整到64M時界轩,GC的CPU占比從27.5%下降到了7.14%,Scavenge算法平均回收耗時減少衔瓮,QPS提升了10%浊猾。繼續(xù)調(diào)大 semi space,性能并沒有顯著提升热鞍,且Scavenge算法回收時間增加葫慎。semi space本身用于新生代對象快速分配,不適合調(diào)整過大碍现。因此幅疼,semi space 設(shè)置為64M較為合適。

總結(jié)

通過將semi space調(diào)大昼接,觸發(fā) Scavenge算法回收的概率降低爽篷,GC的次數(shù)也隨之減少。且 Scavenge算法回收內(nèi)存的時間也較為合理慢睡,因而可以降低GC在CPU中的占比逐工。

本文主要介紹了線上服務(wù)的性能瓶頸的排查與GC調(diào)優(yōu)铡溪,并沒有介紹V8 垃圾回收機制的原理。推薦感興趣的同學(xué)泪喊,閱讀樸靈老師的《深入淺出Node.js》中關(guān)于《V8的垃圾回收機制》一節(jié)棕硫。其中,詳細了介紹了V8用到的各種算法袒啼,非常有助于理解性能調(diào)優(yōu)的原理哈扮。

最后,感謝一下阿里的奕鈞同學(xué)蚓再,在他的幫助下滑肉,幫我解決了問題。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末摘仅,一起剝皮案震驚了整個濱河市靶庙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌娃属,老刑警劉巖六荒,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異矾端,居然都是意外死亡掏击,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門须床,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铐料,“玉大人,你說我怎么就攤上這事豺旬∧瞥停” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵族阅,是天一觀的道長篓跛。 經(jīng)常有香客問我,道長坦刀,這世上最難降的妖魔是什么愧沟? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮鲤遥,結(jié)果婚禮上沐寺,老公的妹妹穿的比我還像新娘。我一直安慰自己盖奈,他們只是感情好混坞,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般究孕。 火紅的嫁衣襯著肌膚如雪啥酱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天厨诸,我揣著相機與錄音镶殷,去河邊找鬼。 笑死微酬,一個胖子當著我的面吹牛绘趋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播得封,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼埋心,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了忙上?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤闲坎,失蹤者是張志新(化名)和其女友劉穎疫粥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腰懂,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡梗逮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了绣溜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慷彤。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怖喻,靈堂內(nèi)的尸體忽然破棺而出底哗,到底是詐尸還是另有隱情,我是刑警寧澤锚沸,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布跋选,位于F島的核電站,受9級特大地震影響哗蜈,放射性物質(zhì)發(fā)生泄漏前标。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一距潘、第九天 我趴在偏房一處隱蔽的房頂上張望炼列。 院中可真熱鬧,春花似錦音比、人聲如沸俭尖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽目溉。三九已至明肮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缭付,已是汗流浹背柿估。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陷猫,地道東北人秫舌。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像绣檬,于是被迫代替她去往敵國和親足陨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容