1. 程式人生 > >MongoDB CPU利用率高,怎麼破?

MongoDB CPU利用率高,怎麼破?

https://help.aliyun.com/document_detail/62224.html

背景資訊

在使用MongoDB雲資料庫的時候您可能經常遇到一個問題:MongoDB CPU利用率很高,都快跑滿了,應該怎麼辦? 遇到這個問題,99.9999%的可能性是您使用上不合理導致。本文主要幫助您從應用的角度排查MongoDB CPU利用率高的問題。

 

分析資料庫正在執行的請求

您可以通過Mongo Shell連線資料庫,並執行db.currentOp()命令,檢視資料庫當前正在執行的操作。如下是該命令的一個輸出示例,標識一個正在執行的操作。

{
        "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"
        },
        ....
    },

重點關注以下幾個欄位:

欄位 說明
client 請求是由哪個客戶端發起的。
opid 操作的opid,有需要的話,可以通過db.killOp(opid) 直接終止該操作。
secs_running/microsecs_running 這個值重點關注,代表請求執行的時間,如果這個值特別大,請看看請求是否合理。
query/ns 這個欄位能看出是對哪個集合正在執行什麼操作。
lock* - 還有一些跟鎖相關的引數,需要了解可以看官網文件,本文不做詳細介紹。
- db.currentOp文件請參見:
db.currentOp 

這裡先要明確一下,您通過db.currentOp()檢視正在執行的操作是否有耗時的請求正在執行。

比如您的業務平時CPU利用率不高,運維管理人員連到資料庫執行了一些需要全表掃描的操作,然後突然CPU利用率飆高,導致你的業務響應很慢,那麼就要重點關注下那些執行時間很長的操作。拿到對應請求的opid,執行db.killOp(opid)終止對應請求。

如果您的應用一上線,cpu利用率就很高,而且一直持續,執行db.currentOp(),結果也沒發現什麼異常請求,可以進行更深入的分析即:分析資料庫慢請求。

 

 

分析資料庫慢請求

MongoDB支援profiling功能,將請求的執行情況記錄到同DB下的system.profile集合裡,profiling有三種模式:

  • 關閉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。

在開啟了慢請求profiling的情況下(MongoDB雲資料庫是預設開啟慢請求profiling的),我們對慢請求的內容進行分析,來找出可優化的點,常見的包括以下幾種場景:

  • 全表掃描(關鍵字:COLLSCAN、 docsExamined)

    • 全集合(表)掃描COLLSCAN,當一個查詢(或更新、刪除)請求需要全表掃描時,是非常耗CPU資源的,所以當你在system.profile集合或者日誌檔案發現COLLSCAN關鍵字時,很可能就是這些查詢佔用了你的CPU資源,如果這種請求比較頻繁,最好是針對查詢的欄位建立索引來優化。

    • 一個查詢掃描了多少文件,可檢視system.profile裡的docsExamined的值,該值越大,請求CPU開銷越大。

  • 不合理的索引(關鍵字:IXSCAN、keysExamined)

    有時請求即使查詢使用了索引,執行也很慢,通常是因為索引建立不太合理(或者是匹配的結果本身就很多,這樣即使使用索引,請求開銷也不會優化很多)。如下所示,假設某個集合的資料,x欄位的取值很少(假設只有1、2),而y欄位的取值很豐富。

     
    1. { x: 1, y: 1 }
    2. { x: 1, y: 2 }
    3. { x: 1, y: 3 }
    4. ......
    5. { x: 1, y: 100000}
    6. { x: 2, y: 1 }
    7. { x: 2, y: 2 }
    8. { x: 2, y: 3 }
    9. ......
    10. { x: 1, y: 100000}

    要實現 {x: 1: y: 2} 這樣的查詢:

     
    1. db.createIndex( {x: 1} ) 效果不好,因為x相同取值太多
    2. db.createIndex( {x: 1, y: 1} ) 效果不好,因為x相同取值太多
    3. db.createIndex( {y: 1 } ) 效果好,因為y相同取值很少
    4. db.createIndex( {y: 1, x: 1 } ) 效果好,因為y相同取值少

    至於{y: 1} 與 {y: 1, x: 1} 的區別,可參考MongoDB索引原理複合索引官方文件

    一個使用了索引的查詢,掃描了多少條索引,可檢視system.profile裡的keysExamined欄位,該值越大,CPU開銷越大。

  • 大量資料排序(關鍵字:SORT、hasSortStage)

    當查詢請求裡包含排序的時候,如果排序無法通過索引滿足,MongoDB會在查詢結果中進行排序,而排序這個動作本身是非常耗CPU資源的,優化的方法仍然是建立索引,對經常需要排序的欄位,建立索引。

    當您在system.profile集合或者日誌檔案發現SORT關鍵字時,就可以考慮通過索引來優化排序。當請求包含排序欄位時,system.profile裡的hasSortStage欄位會為true。

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

總結: 通過找出那些sql 慢,進行優化,建索引