從MySQL遷移到MongoDB記一次MongoDB性能問題詳解

最近忙著把一個項目從mysql遷移到mysql,在導入舊數據的過程中,遇到了些許波折,犯了不少錯誤,但同時也學到了不少知識,遂記錄下來,需要的朋友可以參考下

公司為這個項目專門配備了幾臺高性能務器,清一色的雙路四核超線程CPU,外加32G內存,運維人員安裝好MongoDB后,就交我手里了,我習慣于在使用新服務器前先看看相關日志,了解一下基本情況,當我瀏覽MongoDB日志時,發現一些警告信息:

WARNING:?You?are?running?on?a?NUMA?machine.?We?suggest?launching?mongod?like?this?to?avoid?performance?problems:?numactl?–interleave=all?mongod?[other?options]

當時我并不太清楚NUMA是什么東西,所以沒有處理,只是把問題反饋給了運維人員,后來知道運維人員也沒有理會這茬兒,所以問題的序幕就這樣拉開了。

遷移工作需要導入舊數據。MongoDB本身有一個mongoimport工具可供使用,不過它只接受json、csv等格式的源文件,不適合我的需求,所以我沒用,而是用PHP寫了一個腳本,平穩運行了一段時間后,我發現數據導入的速度下降了,同時PHP拋出異常:

cursor?timed?out?(timeout:?30000,?time?left:?0:0,?status:?0)

我一時判斷不出問題所在,想想先在PHP腳本里加大Timeout的值應付一下:

<?php MongoCursor::$timeout = -1;  ?>

可惜這樣并沒有解決問題,錯誤反倒變著花樣的出現了:

max?number?of?retries?exhausted,?couldn't?send?query,?couldn't?send?query:?Broken?pipe

接著使用strace跟蹤了一下PHP腳本,發現進程卡在了recvfrom操作上:

shell&gt;?strace?-f?-r?-p?<pid>  recvfrom(<fd>,</fd></pid>

通過如下命令mysqlrecvfrom操作的含義:

shell&gt;?apropos?recvfrom  receive?a?message?from?a?socket

或者按照下面的方式確認一下:

shell&gt;?lsof?-p?<pid>  shell&gt;?ls?-l?/proc/<pid>/fd/<fd></fd></pid></pid>

此時如果查詢MongoDB的當前操作,會發現幾乎每個操作會消耗大量的時間:

mongo&gt;?db.currentOp()

與此同時,運行mongostat的話,結果會顯示很高的locked值。

我在網絡上找到一篇:MongoDB Pre-Splitting for Faster Data Loading and Importing,看上去和我的問題很類似,不過他的問題實質是由于自動分片導致數據遷移所致,解決方法是使用手動分片,而我并沒有使用自動分片,自然不是這個原因。

詢問了幾個朋友,有人反映曾遇到過類似的問題,在他的場景里,問題的主要原因是系統IO操作繁忙時,數據文件預分配堵塞了其它操作,從而導致雪崩效應。

為了驗證這種可能,我mysql了一下MongoDB日志:

shell&gt;?grep?FileAllocator?/path/to/log  [FileAllocator]?allocating?new?datafile?...?filling?with?zeroes...  [FileAllocator]?done?allocating?datafile?...?took?...?secs

我使用的mysql是ext4(xfs也不錯 ),創建數據文件非常快,所以不是這個原因,但如果有人使用ext3,可能會遇到這類問題,所以還是大概介紹一下如何解決:

MongoDB按需自動生成數據文件:先是.0,大小是64M,然后是.1,大小翻番到128M,到了.5,大小翻番到2G,其后的數據文件就保持在2G大小。為了避免可能出現的問題,可以采用事先手動創建數據文件的策略:

#!/bin/sh    DB_NAME=$1    cd?/path/to/$DB_NAME    for?INDEX_NUMBER?in?{5..50};?do  ??FILE_NAME=$DB_NAME.$INDEX_NUMBER    ??if?[?!?-e?$FILE_NAME?];?then  ????head?-c?2146435072?/dev/zero?&gt;?$FILE_NAME  ??fi  done

注:數值2146435072并不是標準的2G,這是INT整數范圍決定的。

最后一個求助方式就是官方論壇了,那里的國際友人建議我檢查一下是不是索引不佳所致,死馬當活馬醫,我激活了Profiler記錄慢操作:

mongo&gt;?use?<db>  mongo&gt;?db.setProfilingLevel(1);</db>

不過結果顯示基本都是mysql(因為我是導入數據為主),本身就不需要索引:

mongo&gt;?use?<db>  mongo&gt;?db.system.profile.find().sort({$natural:-1})</db>

問題始終沒有得到解決,求人不如求己,我又重復了幾次遷移舊數據的過程,結果自然還是老樣子,但我發現每當出問題的時候,總有一個名叫irqbalance的進程CPU占用率居高不下,搜索了一下,發現很多介紹irqbalance的文章中都提及了NUMA,讓我一下子想起之前在日志中看到的警告信息,我勒個去,竟然繞了這么大一個圈圈!安下心來仔細翻閱文檔,發現官方其實已經有了相關介紹,按如下設置搞定:

shell&gt;?echo?0?&gt;?/proc/sys/vm/zone_reclaim_mode  shell&gt;?numactl?--interleave=all?mongod?[options]

關于zone_reclaim_mode內核參數的說明,可以參考官方文檔。

注:從MongoDB1.9.2開始:MongoDB會在啟動時自動設置zone_reclaim_mode。

至于NUMA的含義,簡單點說,在有多個物理CPU的架構下,NUMA把內存分為本地和遠程,每個物理CPU都有屬于自己的本地內存,訪問本地內存速度快于訪問遠程內存,缺省情況下,每個物理CPU只能訪問屬于自己的本地內存。對于MongoDB這種需要大內存的服務來說就可能造成內存不足,NUMA的詳細介紹,可以參考老外的文章。

理論上,MySQL、mysqlmysql等等都可能會受到NUMA的影響,需要留意。

? 版權聲明
THE END
喜歡就支持一下吧
點贊14 分享