前言
首先,我們簡單梳理一下,CPU 在什么情況下才算負載較高?負載查看是通過"uptime"命令查看。大家都知道,命令顯示的結果分別表示1分鍾、5分鍾、15分鍾的負載情況,這點就不多做說明。在系統負荷方面,多核CPU與多CPU效果類似,所以考慮系統負荷的時候,必須考慮這台電腦有幾個CPU、每個CPU有幾個核心。然后,把系統負荷除以總的核心數,只要每個核心的負荷不超過1.0,就表明電腦正常運行。從單棵CPU來說,一般負載不超過0.7都無需關系,當超過該值得時候,就應該開始調查了,問題出在哪里,防止情況惡化。
負載計算公式:
[root@mongodb-1219 ~]# grep 'model name' /proc/cpuinfo | wc -l 24 [root@mongodb-1219 ~]# echo "0.7 * 24" |bc 16.8
N個CPU的電腦,可接受的系統負荷最大為n。正常情況為"N * 0.7",該值為可觀狀態。
案例
[root@mongodb-1219 ~]# top top - 09:58:34 up 325 days, 14:15, 5 users, load average: 84.13, 156.16, 108.10 Tasks: 1078 total, 1 running, 1077 sleeping, 0 stopped, 0 zombie %Cpu(s): 2.9 us, 0.2 sy, 0.0 ni, 96.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 32639968 total, 1817952 free, 998372 used, 29823644 buff/cache KiB Swap: 16777212 total, 16773128 free, 4084 used. 29489896 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 333052 root 20 0 34.607g 657716 262228 S 74.3 2.0 209:12.13 mongod 366752 root 20 0 147216 3128 1432 S 1.0 0.0 0:02.50 top 367511 root 20 0 147072 3024 1416 R 1.0 0.0 0:00.19 top 2189 root 20 0 686788 36396 4188 S 0.3 0.1 267:57.64 salt-minion 1 root 20 0 69856 31864 1964 S 0.0 0.1 15:13.08 systemd
如圖所示,該服務器CPU使用高達74%(截圖存在一定的偏差,其實此時的用戶占用CPU值相當高)。根據信息可以得知,是用戶態CPU使用較高,那么這種情況一般都是用戶使用不合理。這種情況,不僅在MongoDB中,MySQL中也會有類似的問題。
Setup 1.查看相關日志
查看日志,發現有條查詢語句竟然耗時7077ms,看樣子有問題。(此時估摸着,就是開發沒有加索引。)
Setup 2.分析數據庫正在執行的請求
用戶可以通過 Mongo Shell 連接,並執行 db.currentOp() 命令,能看到數據庫當前正在執行的操作,如下是該命令的一個輸出示例,標識一個正在執行的操作。重點關注幾個字段:
client:請求是由哪個客戶端發起的;
opid:操作的opid,有需要的話,可以通過 db.killOp(opid) 直接干掉的操作;
secs_running/microsecs_running: 這個值重點關注,代表請求運行的時間,如果這個值特別大,就得注意了,看看請求是否合理;
query/ns: 這個能看出是對哪個集合正在執行什么操作;
lock*:還有一些跟鎖相關的參數,需要了解可以看官網文檔,本文不做詳細介紹;
Setup 3.分析數據庫的慢請求
MongoDB 支持 profiling 功能,將請求的執行情況記錄到同DB下的 system.profile 集合里,profiling 有3種模式:
關閉 profiling
針對所有請求開啟 profiling,將所有請求的執行都記錄到 system.profile 集合
針對慢請求 profiling,將超過一定閾值的請求,記錄到system.profile 集合
默認請求下,MongoDB 的 profiling 功能是關閉,生產環境建議開啟,慢請求閾值可根據需要定制,如不確定,直接使用默認值100ms。
關於profiling功能說明,參考文檔。默認請求下,MongoDB 的 profiling 功能是關閉,生產環境建議開啟,慢請求閾值可根據需要定制,如不確定,直接使用默認值100ms。
operationProfiling: mode: slowOp slowOpThresholdMs: 100
基於上述配置,MongoDB 會將超過 100ms 的請求記錄到對應DB 的 system.profile 集合里,system.profile 默認是一個最多占用 1MB 空間的 capped collection。
查看最近3條 慢請求,{$natrual: -1} 代表按插入數序逆序 db.system.profile.find().sort({$natrual: -1}).limit(3)
情況1:全盤掃描
全集合(表)掃描 COLLSCAN,當一個查詢(或更新、刪除)請求需要全表掃描時,是非常耗CPU資源的,所以當你在 system.profile 集合 或者日志文件發現 COLLSCAN 關鍵字時,就得注意了,很可能就是這些查詢吃掉了你的 CPU 資源;確認一下,如果這種請求比較頻繁,最好是針對查詢的字段建立索引來優化。
一個查詢掃描了多少文檔,可查看 system.profile 里的 docsExamined 的值,該值越大,請求CPU開銷越大。關鍵字:COLLSCAN、 docsExamined。
情況2:索引未添加或不合理
一個走索引的查詢,掃描了多少條索引,可查看 system.profile 里的 keysExamined 字段,該值越大,CPU 開銷越大。關鍵字:IXSCAN、keysExamined。
情況3:大量數據排序
當查詢請求里包含排序的時候,如果排序無法通過索引滿足,MongoDB 會在內存里將結果進行排序,而排序這個動作本身是非常耗 CPU 資源的,優化的方法仍然是建立索引,對經常需要排序的字段,建立索引。當你在 system.profile 集合 或者 日志文件發現 SORT 關鍵字時,就可以考慮通過索引來優化排序。當請求包含排序階段時, system.profile 里的 hasSortStage 字段會為 true。關鍵字:SORT、hasSortStage。
其他還有諸如建索引,aggregationv等操作也可能非常耗 CPU 資源,但本質上也是上述幾種場景;建索引需要全表掃描,而vaggeregation 也是遍歷、查詢、更新、排序等動作的組合。
基本上就是以上幾種情況,還有的話就是MongoDB確實已經達到瓶頸,此時可能需要通過shard來解決。