記一次MongoDB性能問題,附原理解析
下面文章轉(zhuǎn)載自火丁筆記,原作者描述了一次MongoDB數(shù)據(jù)遷移過程中遇到的性能問題及其解決方案,中間追查問題的方法和工具值得我們學(xué)習(xí)。
下面是其原文:
最近忙著把一個(gè)項(xiàng)目從MySQL遷移到MongoDB,在導(dǎo)入舊數(shù)據(jù)的過程中,遇到了些許波折,犯了不少錯(cuò)誤,但同時(shí)也學(xué)到了不少知識(shí),遂記錄下來。
公司為這個(gè)項(xiàng)目專門配備了幾臺(tái)高性能務(wù)器,清一色的雙路四核超線程CPU,外加32G內(nèi)存,運(yùn)維人員安裝好MongoDB后,就輪到我了,我習(xí)慣于在使用新服務(wù)器前先看看相關(guān)日志,了解一下基本情況,當(dāng)我瀏覽MongoDB日志時(shí),發(fā)現(xiàn)一些警告信息:
當(dāng)時(shí)我并不太清楚NUMA是什么東西,所以沒有處理,只是把問題報(bào)告給了運(yùn)維人員,事實(shí)證明運(yùn)維人員也沒有處理,所以問題的序幕就這樣拉開了…
- WARNING: You are running on a NUMA machine.
- We suggest launching mongod like this to avoid performance problems:
- numactl --interleave=all mongod [other options]
遷移工作首先要導(dǎo)入舊數(shù)據(jù)。開始一切倒還正常,不過幾小時(shí)之后,我無意中發(fā)現(xiàn)不知道什么時(shí)候開始數(shù)據(jù)導(dǎo)入的速度下降了,同時(shí)我的PHP腳本開始不停的拋出異常:
- cursor timed out (timeout: 30000, time left: 0:0, status: 0)
我一時(shí)判斷不出問題所在,想想先在PHP腳本里加大Timeout的值應(yīng)付一下:
- MongoCursor::$timeout = -1;
可惜這樣并沒有解決問題,錯(cuò)誤反倒變著花樣的出現(xiàn)了:
無奈之下用strace跟蹤了一下PHP腳本:
- max number of retries exhausted, couldn't send query
- couldn't send query: Broken pipe
- shell> strace -p
發(fā)現(xiàn)進(jìn)程卡在了recvfrom操作上:
- recvfrom(,
通過如下命令查詢r(jià)ecvfrom操作的含義是:receive a message from a socket
- shell> apropos recvfrom
還可以按照下面的方式確認(rèn)一下:
此時(shí)查詢MongoDB當(dāng)前操作,發(fā)現(xiàn)幾乎每個(gè)操作會(huì)消耗大量的時(shí)間:
- shell> lsof -p
- shell> ls -l /proc//fd/
- shell> echo "db.currentOp()" | /path/to/mongo
同時(shí)運(yùn)行mongostat顯示很高的locked值。
…
重復(fù)做了很多工作,但始終無法找到問題的癥結(jié)在哪里,只好求助官方論壇,那里的技術(shù)支持都很熱心,在我描述了問題后,沒過多久就有了回復(fù),建議我檢查一下是不是索引不佳所致,為了驗(yàn)證這種可能,我激活了Profiler記錄慢操作:
不過結(jié)果顯示基本都是insert操作(因?yàn)槲沂菍?dǎo)入數(shù)據(jù)為主),本身就不需要索引:
- mongo> use
- mongo> db.setProfilingLevel(1);
問 題到了這里,似乎已經(jīng)走投無路了,為了死馬當(dāng)活馬醫(yī),我又重復(fù)了幾次遷移舊數(shù)據(jù)的過程,結(jié)果自然是次次都出問題,但幸運(yùn)的是我發(fā)現(xiàn)每當(dāng)出問題的時(shí)候,在 top命令的結(jié)果中,總有一個(gè)名叫irqbalance的進(jìn)程居高不下,搜索了一下,結(jié)果很多介紹irqbalance的文章中都提及了NUMA,讓我一 下子記起之前在日志中看到的警告信息,于是乎按照信息里介紹的,重新啟動(dòng)了一下MongoDB:
- mongo> use
- mongo> db.system.profile.find().sort({$natural:-1})
- …
- shell> numactl --interleave=all /path/to/mongod
一切都正常了。為了解決這個(gè)問題,浪費(fèi)了很多精神,實(shí)在沒有力氣再解釋NUMA到底是什么東西了,有想了解的網(wǎng)友可以參考老外的文章,里面的介紹很翔實(shí)。
對(duì)于罪魁禍?zhǔn)祝髡吡艚o大家去學(xué)習(xí),在這里可以給大家做一個(gè)簡單的描述,先解釋幾個(gè)概念
NUMA:NUMA是多核心CPU架構(gòu)中的一種,其全稱為Non-Uniform Memory Access,簡單來說就是在多核心CPU中,機(jī)器的物理內(nèi)存是分配給各個(gè)核的,架構(gòu)簡圖如下所示:
每個(gè)核訪問分配給自己的內(nèi)存會(huì)比訪問分配給其它核的內(nèi)存要快,有下面幾種訪問控制策略:
- 1.缺省(default):總是在本地節(jié)點(diǎn)分配(分配在當(dāng)前進(jìn)程運(yùn)行的節(jié)點(diǎn)上);
- 2.綁定(bind):強(qiáng)制分配到指定節(jié)點(diǎn)上;
- 3.交叉(interleave):在所有節(jié)點(diǎn)或者指定的節(jié)點(diǎn)上交織分配;
- 4.優(yōu)先(preferred):在指定節(jié)點(diǎn)上分配,失敗則在其他節(jié)點(diǎn)上分配。
上面文章中最后使用numactl –interleave命令就是指定其為交叉共享模式。
irqbalance:這是作者在上面提到的一個(gè)占用CPU的進(jìn)程,這個(gè)進(jìn)程的作用是在多核心CPU的操作系統(tǒng)中,分配系統(tǒng)中斷信號(hào)的。參見:irqbalance.org
概念說完了,下面是上面問題的簡單描述:
我們知道虛擬內(nèi)存機(jī)制是通過一個(gè)中斷信號(hào)來通過進(jìn)行內(nèi)存swap的,所以這個(gè)irqbalance進(jìn)程忙,是一個(gè)危險(xiǎn)信號(hào),在這里是由于在進(jìn)行頻繁的內(nèi)存交換。這種頻繁交換現(xiàn)象稱為swap insanity,在MySQL中經(jīng)常提到,也就是在NUMA框架中,采用不合適的策略,導(dǎo)致核心只能從指定內(nèi)存塊節(jié)點(diǎn)上分配內(nèi)存,即使總內(nèi)存還有富余,也會(huì)由于當(dāng)前節(jié)點(diǎn)內(nèi)存不足時(shí)產(chǎn)生大量的swap操作。
【編輯推薦】





























