MongoDB CPU利用率很高,怎麼破(轉)

常常有用戶諮詢:MongoDB CPU 利用率很高,都快跑滿了,應該怎麼辦?數據庫

遇到這個問題,99.9999% 的可能性是「用戶使用上不合理致使」,本文主要介紹從應用的角度如何排查 MongoDB CPU 利用率高的問題。app

Step1: 分析數據庫正在執行的請求運維

用戶能夠經過 Mongo Shell 鏈接,並執行 db.currentOp() 命令,能看到數據庫當前正在執行的操做,以下是該命令的一個輸出示例,標識一個正在執行的操做。重點關注幾個字段:測試

  • client:請求是由哪一個客戶端發起的?
  • opid:操做的opid,有須要的話,能夠經過 db.killOp(opid) 直接幹掉的操做;
  • secs_running/microsecs_running: 這個值重點關注,表明請求運行的時間,若是這個值特別大,就得注意了,看看請求是否合理;
  • query/ns: 這個能看出是對哪一個集合正在執行什麼操做;
  • lock*:還有一些跟鎖相關的參數,須要瞭解能夠看官網文檔,本文不作詳細介紹。
{
        "desc" : "conn632530",
        "threadId" : "140298196924160",
        "connectionId" : 632530,
        "client" : "11.192.159.236:57052",
        "active" : true,
        "opid" : 1008837885,
        "secs_running" : 0,
        "microsecs_running" : NumberLong(70),
        "op" : "update",
        "ns" : "mygame.players",
        "query" : {
            "uid" : NumberLong(31577677)
        },
        "numYields" : 0,
        "locks" : {
            "Global" : "w",
            "Database" : "w",
            "Collection" : "w"
        },
        ....
},

這裏先要明確一下,經過 db.currentOp() 查看正在執行的操做,目的究竟是什麼?優化

並非說咱們要將正在執行的操做都列出來,而後經過 killOp 逐個幹掉;這一步的目的是要看一下,是否有「意料以外」的耗時請求正在執行。ui

好比你的業務平時 CPU 利用率不高,運維管理人員連到數據庫執行了一些須要全表掃描的操做,而後忽然 CPU 利用率飆高,致使你的業務響應很慢,那麼就要重點關注下那些執行時間很長的操做。spa

一旦找到罪魁禍首,拿到對應請求的 opid,執行 db.killOp(opid) 將對應的請求幹掉。日誌

若是你的應用一上線,cpu利用率就很高,並且一直持續,經過 db.currentOp 的結果也沒發現什麼異常請求,能夠進入到 Step2 進行更深刻的分析。code

Step2:分析數據庫慢請求排序

MongoDB 支持 profiling 功能,將請求的執行狀況記錄到同DB下的 system.profile 集合裏,profiling 有3種模式:

  • 關閉 profiling;
  • 針對全部請求開啓 profiling,將全部請求的執行都記錄到 system.profile 集合;
  • 針對慢請求 profiling,將超過必定閾值的請求,記錄到system.profile 集合。

默認請求下,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)

在開啓了慢請求 profiling 的狀況下(MongoDB 雲數據庫是默認開啓慢請求 profiling的),咱們對慢請求的內容進行分析,來找出可優化的點,常見的包括。

CPU殺手1:全表掃描

全集合(表)掃描 COLLSCAN,當一個查詢(或更新、刪除)請求須要全表掃描時,是很是耗CPU資源的,因此當你在 system.profile 集合 或者 日誌文件發現 COLLSCAN 關鍵字時,就得注意了,極可能就是這些查詢吃掉了你的 CPU 資源;確認一下,若是這種請求比較頻繁,最好是針對查詢的字段創建索引來優化。

一個查詢掃描了多少文檔,可查看 system.profile 裏的 docsExamined 的值,該值越大,請求CPU開銷越大。

> 關鍵字:COLLSCAN、 docsExamined

CPU殺手2:不合理的索引

有的時候,請求即便查詢走了索引,執行也很慢,一般是由於合理創建不太合理(或者是匹配的結果自己就不少,這樣即便走索引,請求開銷也不會優化不少)。

以下所示,假設某個集合的數據,x字段的取值不多(假設只有一、2),而y字段的取值很豐富。

{ x: 1, y: 1 }
{ x: 1, y: 2 }
{ x: 1, y: 3 }
......
{ x: 1, y: 100000} 
{ x: 2, y: 1 }
{ x: 2, y: 2 }
{ x: 2, y: 3 }
......
{ x: 1, y: 100000}

要服務 {x: 1: y: 2} 這樣的查詢

db.createIndex( {x: 1} )         效果很差,由於x相同取值太多;
db.createIndex( {x: 1, y: 1} )   效果很差,由於x相同取值太多;
db.createIndex( {y: 1 } )        效果好,由於y相同取值不多;
db.createIndex( {y: 1, x: 1 } )  效果好,由於y相同取值少;

至於{y: 1} 與 {y: 1, x: 1} 的區別,可參考MongoDB索引原理 及 複合索引官方文檔 自行理解。

一個走索引的查詢,掃描了多少條索引,可查看 system.profile 裏的 keysExamined 字段,該值越大,CPU 開銷越大。

>關鍵字:IXSCAN、keysExamined

CPU殺手3:大量數據排序

當查詢請求裏包含排序的時候,若是排序沒法經過索引知足,MongoDB 會在內存李結果進行排序,而排序這個動做自己是很是耗 CPU 資源的,優化的方法仍然是創建索引,對常常須要排序的字段,創建索引。

當你在 system.profile 集合 或者 日誌文件發現 SORT 關鍵字時,就能夠考慮經過索引來優化排序。當請求包含排序階段時, system.profile 裏的 hasSortStage 字段會爲 true。

> 關鍵字:SORT、hasSortStage

其餘還有諸如建索引,aggregationv等操做也可能很是耗 CPU 資源,但本質上也是上述幾種場景;建索引須要全表掃描,而vaggeregation 也是遍歷、查詢、更新、排序等動做的組合。

Step3: 服務能力評估

通過上述2步,你發現整個數據庫的查詢很是合理,全部的請求都是高效的走了索引,基本沒有優化的空間了,那麼極可能是你機器的服務能力已經達到上限了,應該升級配置了(或者經過 sharding 擴展)。

固然最好的狀況時,提早對 MongoDB 進行測試,瞭解在你的場景下,對應的服務能力上限,以便及時擴容、升級,而不是到 CPU 資源用滿,業務已經徹底撐不住的時候纔去作評估。

 

來源:https://www.ywnds.com/?p=9010

文章來源:張友東的博客

相關文章
相關標籤/搜索