排查MongoDB CPU使用率高的問(wèn)題
在使用云數(shù)據(jù)庫(kù)MongoDB的時(shí)候您可能會(huì)遇到MongoDB CPU使用率很高或者CPU使用率接近100%的問(wèn)題,從而導(dǎo)致數(shù)據(jù)讀寫處理異常緩慢,影響正常業(yè)務(wù)。本文主要幫助您從應(yīng)用的角度排查MongoDB CPU使用率高的問(wèn)題。
分析MongoDB數(shù)據(jù)庫(kù)正在執(zhí)行的請(qǐng)求
通過(guò)Mongo Shell連接實(shí)例。
執(zhí)行
db.currentOp()
命令,查看數(shù)據(jù)庫(kù)當(dāng)前正在執(zhí)行的操作。回顯信息如下:
{ "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" }, .... }
您需要重點(diǎn)關(guān)注如下字段:
字段
說(shuō)明
client
發(fā)起請(qǐng)求的客戶端地址。
opid
識(shí)別當(dāng)前操作的標(biāo)識(shí)符。
如果需要終止當(dāng)前操作,您可以通過(guò)執(zhí)行
db.killOp(opid)
終止。secs_running
當(dāng)前操作已經(jīng)執(zhí)行的時(shí)間,單位:秒。
如果已經(jīng)執(zhí)行的時(shí)間較長(zhǎng),建議您查看請(qǐng)求是否合理。
microsecs_running
當(dāng)前操作已經(jīng)執(zhí)行的時(shí)間,單位:微秒。
如果已經(jīng)執(zhí)行的時(shí)間較長(zhǎng),建議您查看請(qǐng)求是否合理。
ns
當(dāng)前操作的目標(biāo)集合。
op
當(dāng)前操作的類型,通常是查詢、插入、更新和刪除中的一種。
locks
跟鎖相關(guān)的信息,詳情請(qǐng)參見(jiàn)并發(fā)介紹。
說(shuō)明db.currentOp()
命令的更多信息,請(qǐng)參見(jiàn)db.currentOp()。
您可以通過(guò)db.currentOp()
命令查看當(dāng)前正在執(zhí)行的操作,分析是否有不正常耗時(shí)的請(qǐng)求正在執(zhí)行。例如您的業(yè)務(wù)平時(shí)CPU使用率不高,運(yùn)維管理人員連到MongoDB數(shù)據(jù)庫(kù)執(zhí)行了一些需要全表掃描的操作導(dǎo)致CPU使用率非常高,業(yè)務(wù)響應(yīng)緩慢,此時(shí)需要重點(diǎn)關(guān)注執(zhí)行時(shí)間非常耗時(shí)的操作。
如果發(fā)現(xiàn)有異常的請(qǐng)求,您可以找到該請(qǐng)求對(duì)應(yīng)的opid
,執(zhí)行db.killOp(opid)
終止該請(qǐng)求。
db.killOp()
命令的更多信息,請(qǐng)參見(jiàn)db.killOp()。
分析MongoDB數(shù)據(jù)庫(kù)的慢請(qǐng)求
如果您的應(yīng)用剛剛上線,MongoDB實(shí)例的CPU使用率馬上處于持續(xù)很高的狀態(tài),并且執(zhí)行db.currentOp()
命令后,在輸出結(jié)果中未發(fā)現(xiàn)異常請(qǐng)求,您需要分析數(shù)據(jù)庫(kù)的慢請(qǐng)求。
在控制臺(tái)查看慢日志。如何查看,請(qǐng)參見(jiàn)查看慢日志。
分析慢請(qǐng)求日志,查找引起MongoDB實(shí)例的CPU使用率升高的原因。
以下為某個(gè)慢日志示例:
{ "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" }
通常在慢請(qǐng)求日志中,您需要重點(diǎn)關(guān)注如下信息:
全表掃描(關(guān)鍵字:
COLLSCAN
、docsExamined
)全集合(表)掃描
COLLSCAN
。當(dāng)一個(gè)操作請(qǐng)求(如查詢、更新、刪除等)需要全表掃描時(shí),將非常占用CPU資源。在查看慢請(qǐng)求日志時(shí)發(fā)現(xiàn)
COLLSCAN
關(guān)鍵字,很可能是這些查詢占用了CPU資源。說(shuō)明如果這種請(qǐng)求比較頻繁,建議對(duì)查詢的字段建立索引的方式來(lái)優(yōu)化。
通過(guò)查看
docsExamined
的值,可以查看到一個(gè)查詢掃描了多少文檔。該值越大,請(qǐng)求所占用的CPU開(kāi)銷越大。
不合理的索引(關(guān)鍵字:
IXSCAN
、keysExamined
)說(shuō)明索引不是越多越好,索引過(guò)多會(huì)影響寫入、更新的性能。
如果您的應(yīng)用偏向于寫操作,索引可能會(huì)影響性能。
通過(guò)查看
keysExamined
字段,可以查看到一個(gè)使用了索引的查詢,掃描了多少條索引。該值越大,CPU開(kāi)銷越大。如果索引建立的不太合理,或者是匹配的結(jié)果很多,這樣即使使用索引,請(qǐng)求開(kāi)銷也不會(huì)優(yōu)化很多,執(zhí)行的速度也會(huì)很慢。
如下所示,假設(shè)某個(gè)集合的數(shù)據(jù),x字段取值的重復(fù)率很高(假設(shè)只有1、2),而y字段取值的重復(fù)率很低。
{ 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}
要實(shí)現(xiàn) {x: 1, y: 2} 這樣的查詢。
db.createIndex( {x: 1} ) //效果不好,因?yàn)?span id="z68uejxpaoma" class="help-letter-space">x相同取值太多 db.createIndex( {x: 1, y: 1} ) //效果不好,因?yàn)?span id="z68uejxpaoma" class="help-letter-space">x相同取值太多 db.createIndex( {y: 1 } ) //效果好,因?yàn)?span id="z68uejxpaoma" class="help-letter-space">y相同取值很少 db.createIndex( {y: 1, x: 1 } ) //效果好,因?yàn)?span id="z68uejxpaoma" class="help-letter-space">y相同取值很少
說(shuō)明關(guān)于{y: 1}與{y: 1, x: 1}的區(qū)別,請(qǐng)參見(jiàn)MongoDB索引原理及復(fù)合索引官方文檔。
大量數(shù)據(jù)排序(關(guān)鍵字:
SORT
、hasSortStage
)當(dāng)查詢請(qǐng)求里包含排序的時(shí)候, 請(qǐng)求中的
hasSortStage
字段會(huì)為true
。如果排序無(wú)法通過(guò)索引滿足,MongoDB會(huì)在查詢結(jié)果中進(jìn)行排序,而排序這個(gè)動(dòng)作將非常消耗CPU資源,這種情況需要對(duì)經(jīng)常排序的字段建立索引的方式進(jìn)行優(yōu)化。說(shuō)明當(dāng)您在慢日志里發(fā)現(xiàn)
SORT
關(guān)鍵字時(shí),可以考慮通過(guò)索引來(lái)優(yōu)化排序。
其他還有諸如建立索引、aggregation
(遍歷、查詢、更新、排序等動(dòng)作的組合)等操作也可能非常耗CPU資源,但本質(zhì)上也是上述幾種場(chǎng)景。
您也可以將MongoDB實(shí)例接入至數(shù)據(jù)庫(kù)自治服務(wù)DAS(Database Autonomy Service)。在DAS控制臺(tái)中,您可以對(duì)MongoDB實(shí)例的實(shí)時(shí)性能、實(shí)時(shí)會(huì)話、慢日志、磁盤空間等信息進(jìn)行監(jiān)控和管理,詳情請(qǐng)參見(jiàn)DAS操作流程。
服務(wù)能力評(píng)估
經(jīng)過(guò)上述分析數(shù)據(jù)庫(kù)正在執(zhí)行的請(qǐng)求和分析數(shù)據(jù)庫(kù)慢請(qǐng)求兩輪優(yōu)化之后,整個(gè)數(shù)據(jù)庫(kù)的查詢相對(duì)合理,所有的請(qǐng)求都高效地使用了索引。如果經(jīng)過(guò)兩輪優(yōu)化后,還存在CPU資源被占滿的問(wèn)題,則可能是實(shí)例的服務(wù)能力已經(jīng)達(dá)到上限導(dǎo)致,建議您通過(guò)如下方法解決:
通過(guò)查看監(jiān)控信息分析實(shí)例資源的使用狀態(tài),詳情請(qǐng)參見(jiàn)查看監(jiān)控信息、基本監(jiān)控和高級(jí)監(jiān)控。
對(duì)MongoDB數(shù)據(jù)庫(kù)進(jìn)行測(cè)試,便于您了解在您的業(yè)務(wù)場(chǎng)景下,當(dāng)前實(shí)例是否滿足所需要的設(shè)備性能和服務(wù)能力。
如果您需要升級(jí)實(shí)例,可以參考變更配置或變更副本集實(shí)例節(jié)點(diǎn)數(shù)進(jìn)行操作。