在使用ApsaraDB for MongoDB的時候您可能會遇到MongoDB CPU使用率很高或者CPU使用率接近100%的問題,從而導致資料讀寫處理異常緩慢,影響正常業務。本文主要協助您從應用的角度排查MongoDB CPU使用率高的問題。
分析MongoDB資料庫正在執行的請求
通過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
識別當前操作的標識符。
如果需要終止當前操作,您可以通過執行
db.killOp(opid)
終止。secs_running
當前操作已經執行的時間,單位:秒。
如果已經執行的時間較長,建議您查看請求是否合理。
microsecs_running
當前操作已經執行的時間,單位:微秒。
如果已經執行的時間較長,建議您查看請求是否合理。
ns
當前操作的目的地組合。
op
當前操作的類型,通常是查詢、插入、更新和刪除中的一種。
locks
跟鎖相關的資訊,詳情請參見並發介紹。
說明db.currentOp()
命令的更多資訊,請參見db.currentOp()。
您可以通過db.currentOp()
命令查看當前正在執行的操作,分析是否有不正常耗時的請求正在執行。例如您的業務平時CPU使用率不高,營運管理員連到MongoDB資料庫執行了一些需要全表掃描的操作導致CPU使用率非常高,業務響應緩慢,此時需要重點關注執行時間非常耗時的操作。
如果發現有異常的請求,您可以找到該請求對應的opid
,執行db.killOp(opid)
終止該請求。
db.killOp()
命令的更多資訊,請參見db.killOp()。
分析MongoDB資料庫的慢請求
如果您的應用剛剛上線,MongoDB執行個體的CPU使用率馬上處於持續很高的狀態,並且執行db.currentOp()
命令後,在輸出結果中未發現異常請求,您需要分析資料庫的慢請求。
在控制台查看慢日誌。如何查看,請參見查看慢日誌。
分析慢請求日誌,尋找引起MongoDB執行個體的CPU使用率升高的原因。
以下為某個慢日誌樣本:
{ "atype": "slowOp", "param": { "op": "query", "ns": "abbott_analysis.uaidScanInfo", "query": { "find": "uaidScanInfo", "filter": { "dateType": 2, "companyCode": "GMP" }, "ntoreturn": -1, "sort": { "scanDateTime": -1 } }, "keysExamined": 0, "docsExamined": 2181021, "hasSortStage": true, "cursorExhausted": true, "numYield": 17059, "locks": { "Global": { "acquireCount": { "r": { "$numberLong": "34120" } }, "acquireWaitCount": { "r": { "$numberLong": "7" } }, "timeAcquiringMicros": { "r": { "$numberLong": "3152" } } }, "Database": { "acquireCount": { "r": { "$numberLong": "17060" } } }, "Collection": { "acquireCount": { "r": { "$numberLong": "17060" } } } }, "nreturned": 0, "responseLength": 20, "millis": 4878, "planSummary": "COLLSCAN" }, "result": "OK" }
通常在慢請求日誌中,您需要重點關注如下資訊:
全表掃描(關鍵字:
COLLSCAN
、docsExamined
)全集合(表)掃描
COLLSCAN
。當一個操作請求(如查詢、更新、刪除等)需要全表掃描時,將非常佔用CPU資源。在查看慢請求日誌時發現
COLLSCAN
關鍵字,很可能是這些查詢佔用了CPU資源。說明如果這種請求比較頻繁,建議對查詢的欄位建立索引的方式來最佳化。
通過查看
docsExamined
的值,可以查看到一個查詢掃描了多少文檔。該值越大,請求所佔用的CPU開銷越大。
不合理的索引(關鍵字:
IXSCAN
、keysExamined
)說明索引不是越多越好,索引過多會影響寫入、更新的效能。
如果您的應用偏向於寫操作,索引可能會影響效能。
通過查看
keysExamined
欄位,可以查看到一個使用了索引的查詢,掃描了多少條索引。該值越大,CPU開銷越大。如果索引建立的不太合理,或者是匹配的結果很多,這樣即使使用索引,請求開銷也不會最佳化很多,執行的速度也會很慢。
如下所示,假設某個集合的資料,x欄位取值的重複率很高(假設只有1、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索引原理及複合索引官方文檔。
大量資料排序(關鍵字:
SORT
、hasSortStage
)當查詢請求裡包含排序的時候, 請求中的
hasSortStage
欄位會為true
。如果排序無法通過索引滿足,MongoDB會在查詢結果中進行排序,而排序這個動作將非常消耗CPU資源,這種情況需要對經常排序的欄位建立索引的方式進行最佳化。說明當您在慢日誌裡發現
SORT
關鍵字時,可以考慮通過索引來最佳化排序。
其他還有諸如建立索引、aggregation
(遍曆、查詢、更新、排序等動作的組合)等操作也可能非常耗CPU資源,但本質上也是上述幾種情境。
服務能力評估
經過上述分析資料庫正在執行的請求和分析資料庫慢請求兩輪最佳化之後,整個資料庫的查詢相對合理,所有的請求都高效地使用了索引。如果經過兩輪最佳化後,還存在CPU資源被佔滿的問題,則可能是執行個體的服務能力已經達到上限導致,建議您通過如下方法解決:
如果您需要升級執行個體,可以參考變更配置或變更複本集執行個體節點數進行操作。