爲何要監控?mongodb
監控及時得到應用的運行狀態信息,在問題出現時及時發現。數據庫
監控什麼?數組
CPU、內存、磁盤I/O、應用程序(MongoDB)、進程監控(ps -aux)、錯誤日誌監控app
1.4.1 MongoDB集羣監控方式
查看實例運行狀態(內存使用、鎖、用戶鏈接等信息)post
經過比對先後快照進行性能分析性能
"connections" # 當前鏈接到本機處於活動狀態的鏈接數
"activeClients" # 鏈接到當前實例處於活動狀態的客戶端數量
"locks" # 鎖相關參數
"opcounters" # 啓動以後的參數
"opcountersRepl" # 複製想關
"storageEngine" # 查看數據庫的存儲引擎
"mem" # 內存相關
顯示信息說明:優化
# 統計數據庫信息
db.stats()
{
"db" : "test", # 系統自帶測試數據庫
"collections" : 0, # 集合數量
"views" : 0, #
"objects" : 0, # 文檔對象的個數, 全部集合的記錄數之和
"avgObjSize" : 0, # 平均每一個對象的大小, 經過 dataSize / Objects 獲得
"dataSize" : 0, # 當前庫全部集合的數據大小
"storageSize" : 0, # 磁盤存儲大小
"numExtents" : 0, # 全部集合的擴展數據量統計數
"indexes" : 0, # 已創建索引數量
"indexSize" : 0, # 索引大小
"fileSize" : 0, #
"fsUsedSize" : 0, #
"fsTotalSize" : 0, #總大小
"ok" : 1
}
1.4.2 mongostat
實時數據庫狀態,讀寫、加鎖、索引命中、缺頁中斷、讀寫等待隊列等狀況。ui
每秒刷新一次狀態值,並能提供良好的可讀性,經過這些參數能夠觀察到MongoDB系統總體性能狀況。this
[mongod@MongoDB oplog]$ mongostat -h 10.0.0.152 --port 28017
insert query update delete getmore command flushes mapped vsize res faults qr|qw ar|aw netIn netOut conn set repl time
*0 *0 *0 *0 0 1|0 0 303.0M 13.0M 0 0|0 0|0 143b 8k 1 RTR 2018-01-08T17:28:42+08:00
參數說明:
參數 |
參數說明 |
insert |
每秒插入量 |
query |
每秒查詢量 |
update |
每秒更新量 |
delete |
每秒刪除量 |
conn |
當前鏈接數 |
qr|qw |
客戶端查詢排隊長度(讀|寫)最好爲0,若是有堆積,數據庫處理慢。 |
ar|aw |
活躍客戶端數量(讀|寫) |
time |
當前時間 |
1.4.2 mongotop
mongotop命令說明:
[mongod@MongoDB oplog]$ mongotop -h 127.0.0.1:27017
2018-01-08T17:32:56.623+0800 connected to: 127.0.0.1:27017
ns total read write 2018-01-08T17:32:57+08:00
admin.system.roles 0ms 0ms 0ms
admin.system.users 0ms 0ms 0ms
admin.system.version 0ms 0ms 0ms
app.user 0ms 0ms 0ms
automationcore.automation.job.status 0ms 0ms 0ms
automationcore.config.automation 0ms 0ms 0ms
automationcore.config.automationTemplates 0ms 0ms 0ms
automationcore.config.automationTemplates_archive 0ms 0ms 0ms
automationcore.config.automation_archive 0ms 0ms 0ms
automationstatus.lastAgentStatus 0ms 0ms 0ms
mongotop重要指標
ns:數據庫命名空間,後者結合了數據庫名稱和集合。
total:mongod在這個命令空間上花費的總時間。
read:在這個命令空間上mongod執行讀操做花費的時間。
write:在這個命名空間上mongod進行寫操做花費的時間。
1.4.3 db級別命令
一、db.currentOp()
db.currentOp是個好東西,顧名思義,就是當前的操做。在mongodb中能夠查看當前數據庫上此刻的操做語句信息,包括insert/query/update/remove/getmore/command等多種操做。直接執行
db.currentOp()通常返回一個空的數組,咱們能夠指定一個參數true,這樣就返回用戶connections與系統cmmand相關的操做。下面看個列子:
db.currentOp(true) 會返回不少信息:
用於查看長時間運行進程。
經過(執行時長、操做、鎖、等待鎖時長)等條件過濾。
二、db.killOp(opid)
kill當前的操做 opid爲具體的操做id號,固然了,只能kill正在進行中的。opid是經過db.currentOp()查詢返回的。若是發現一個操做太長,把數據庫卡死的話,能夠用這個命令殺死他:> db.killOp(608605)
示例:
{ "inprog" :
[
{
"opid" : 3434473,//操做的id
"active" : <boolean>,//是否處於活動狀態
"secs_running" : 0,//操做運行了多少秒
"op" : "<operation>",//具體的操做行爲,包括(insert/query/update/remove/getmore/command)
"ns" : "<database>.<collection>",//操做的命名空間,如:數據庫名.集合名
"query" : {//具體的操做語句
},
"client" : "<host>:<outgoing>",//鏈接的客戶端信息
"desc" : "conn57683",//數據庫鏈接描述
"threadId" : "0x7f04a637b700",//線程id
"connectionId" : 57683,//數據庫鏈接id
"locks" : {//鎖的相關信息
"^" : "w",
"^local" : "W",
"^<database>" : "W"
},
"waitingForLock" : false,//是否在等待並獲取鎖,
"msg": "<string>"
"numYields" : 0,
"progress" : {
"done" : <number>,
"total" : <number>
}
"lockStats" : {
"timeLockedMicros" : {//此操做得到如下鎖後,把持的微秒時間
"R" : NumberLong(),//整個mongodb服務實例的全局讀鎖
"W" : NumberLong(),//整個mongodb服務實例的全局寫鎖
"r" : NumberLong(),//某個數據庫實例的讀鎖
"w" : NumberLong() //某個數據庫實例的寫鎖
},
"timeAcquiringMicros" : {//此操做爲了得到如下的鎖,而耗費等待的微秒時間
"R" : NumberLong(),//整個mongodb服務實例的全局讀鎖
"W" : NumberLong(),//整個mongodb服務實例的全局寫鎖
"r" : NumberLong(),//某個數據庫實例的讀鎖
"w" : NumberLong()//某個數據庫實例的寫鎖
}
}
},
.....
]
}
1.4.3 MongoDB優化器profile
在MySQL 中,慢查詢日誌是常常做爲咱們優化數據庫的依據,那在MongoDB 中是否有相似的功能呢?答案是確定的,那就是MongoDB Database Profiler。
1.開啓profiling功能
有兩種方式能夠控制 Profiling 的開關和級別,第一種是直接在啓動參數裏直接進行設置。啓動MongoDB 時加上–profile=級別 便可。也能夠在客戶端調用db.setProfilingLevel(級別) 命令來實時配置,Profiler 信息保存在system.profile 中。咱們能夠經過db.getProfilingLevel()命令來獲取當前的Profile 級別,相似以下操做:
上面profile 的級別能夠取0,1,2 三個值,他們表示的意義以下:
0 – 不開啓
1 – 記錄慢命令 (默認爲>100ms)
2 – 記錄全部命令
Profile 記錄在級別1 時會記錄慢命令,那麼這個慢的定義是什麼?上面咱們說到其默認爲100ms,固然有默認就有設置,其設置方法和級別同樣有兩種,一種是經過添加 –slowms 啓動參數配置。第二種是調用db.setProfilingLevel 時加上第二個參數:
db.setProfilingLevel( level , slowms )
db.setProfilingLevel( 1 , 10 );
2.查看 Profile的設置級別
db.getProfilingLevel()
> db.setProfilingLevel(1)
{ "was" : 2, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
> db.getProfilingLevel()
1
>
3.查詢 Profiling 記錄
與MySQL的慢查詢日誌不一樣,Mongo Profile 記錄是直接存在系統db裏的,記錄位置 system.profile ,因此,咱們只要查詢這個Collection的記錄就能夠獲取到咱們的 Profile 記錄了。
慢查詢(system.profile)說明
經過下面的例子說明,更多信息見:http://docs.mongodb.org/manual/reference/database-profiler/
1:參數含義
drug:PRIMARY> db.system.profile.find().pretty()
{
"op" : "query", #操做類型,有insert、query、update、remove、getmore、command
"ns" : "mc.user", #操做的集合
"query" : { #查詢語句
"mp_id" : 5,
"is_fans" : 1,
"latestTime" : {
"$ne" : 0
},
"latestMsgId" : {
"$gt" : 0
},
"$where" : "new Date(this.latestNormalTime)>new Date(this.replyTime)"
},
"cursorid" : NumberLong("1475423943124458998"),
"ntoreturn" : 0, #返回的記錄數。例如,profile命令將返回一個文檔(一個結果文件),所以ntoreturn值將爲1。limit(5)命令將返回五個文件,所以ntoreturn值是5。若是ntoreturn值爲0,則該命令沒有指定一些文件返回,由於會是這樣一個簡單的find()命令沒有指定的限制。
"ntoskip" : 0, #skip()方法指定的跳躍數
"nscanned" : 304, #掃描數量
"keyUpdates" : 0, #索引更新的數量,改變一個索引鍵帶有一個小的性能開銷,由於數據庫必須刪除舊的key,並插入一個新的key到B-樹索引
"numYield" : 0, #該查詢爲其餘查詢讓出鎖的次數
"lockStats" : { #鎖信息,R:全局讀鎖;W:全局寫鎖;r:特定數據庫的讀鎖;w:特定數據庫的寫鎖
"timeLockedMicros" : { #鎖
"r" : NumberLong(19467),
"w" : NumberLong(0)
},
"timeAcquiringMicros" : { #鎖等待
"r" : NumberLong(7),
"w" : NumberLong(9)
}
},
"nreturned" : 101, #返回的數量
"responseLength" : 74659, #響應字節長度
"millis" : 19, #消耗的時間(毫秒)
"ts" : ISODate("2014-02-25T02:13:54.899Z"), #語句執行的時間
"client" : "127.0.0.1", #連接ip或則主機
"allUsers" : [ ],
"user" : "" #用戶
}
除上面外還有:
scanAndOrder:
scanAndOrder是一個布爾值,是True當一個查詢不能使用的文件的順序在索引中的排序返回結果:MongoDB中必須將其接收到的文件從一個遊標後的文件進行排序。
若是scanAndOrder是False,MongoDB的可以使用這些文件的順序索引返回排序的結果。即:True:文檔進行排序,False:使用索引。
moved
更新操做在磁盤上移動一個或多個文件到新的位置。代表本次update是否移動了硬盤上的數據,若是新記錄比原記錄短,一般不會移動當前記錄,若是新記錄比原記錄長,那麼可能會移動記錄到其它位置,這時候會致使相關索引的更新.磁盤操做更多,加上索引
更新,會使得這樣的操做比較慢.
nmoved:
文件在磁盤上操做。
nupdated:
更新文檔的數目
getmore是一個getmore 操做,getmore一般發生在結果集比較大的查詢時,第一個query返回了部分結果,後續的結果是經過getmore來獲取的。
若是nscanned(掃描的記錄數)遠大於nreturned(返回結果的記錄數)的話,要考慮經過加索引來優化記錄定位了。responseLength 若是過大,說明返回的結果集太大了,這時要看是否只須要必要的字段。
2:平常使用的查詢
#返回最近的10條記錄
db.system.profile.find().limit(10).sort({ ts : -1 }).pretty()
#返回全部的操做,除command類型的
db.system.profile.find( { op: { $ne : 'command' } } ).pretty()
#返回特定集合
db.system.profile.find( { ns : 'mydb.test' } ).pretty()
#返回大於5毫秒慢的操做
db.system.profile.find( { millis : { $gt : 5 } } ).pretty()
#從一個特定的時間範圍內返回信息
db.system.profile.find(
{
ts : {
$gt : new ISODate("2012-12-09T03:00:00Z") ,
$lt : new ISODate("2012-12-09T03:40:00Z")
}
}
).pretty()
#特定時間,限制用戶,按照消耗時間排序
db.system.profile.find(
{
ts : {
$gt : new ISODate("2011-07-12T03:00:00Z") ,
$lt : new ISODate("2011-07-12T03:40:00Z")
}
},
{ user : 0 }
).sort( { millis : -1 } )
> db.system.profile.find() {"ts" : "Thu Jan 29 2009 15:19:32 GMT-0500 (EST)" , "info" : "query test.$cmd ntoreturn:1 reslen:66 nscanned:0 query: { profile: 2 } nreturned:1 bytes:50" , "millis" : 0} db.system.profile.find( { info: /test.foo/ } ) {"ts" : "Thu Jan 29 2009 15:19:40 GMT-0500 (EST)" , "info" : "insert test.foo" , "millis" : 0} {"ts" : "Thu Jan 29 2009 15:19:42 GMT-0500 (EST)" , "info" : "insert test.foo" , "millis" : 0} {"ts" : "Thu Jan 29 2009 15:19:45 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 reslen:102 nscanned:2 query: {} nreturned:2 bytes:86" , "millis" : 0} {"ts" : "Thu Jan 29 2009 15:21:17 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 reslen:36 nscanned:2 query: { $not: { x: 2 } } nreturned:0 bytes:20" , "millis" : 0} {"ts" : "Thu Jan 29 2009 15:21:27 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 exception bytes:53" , "millis" : 88} |
列出執行時間長於某一限度(5ms)的 Profile 記錄:
> db.system.profile.find( { millis : { $gt : 5 } } ) {"ts" : "Thu Jan 29 2009 15:21:27 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 exception bytes:53" , "millis" : 88} |
查看最新的 Profile 記錄:
db.system.profile.find().sort({$natural:-1})
Mongo Shell 還提供了一個比較簡潔的命令show profile,可列出最近5條執行時間超過1ms的 Profile 記錄。
Profile 信息內容詳解:
ts-該命令在什麼時候執行.
millis Time-該命令執行耗時,以毫秒記.
info-本命令的詳細信息.
query-代表這是一個query查詢操做.
ntoreturn-本次查詢客戶端要求返回的記錄數.好比, findOne()命令執行時 ntoreturn 爲 1.有limit(n) 條件時ntoreturn爲n.
query-具體的查詢條件(如x>3).
nscanned-本次查詢掃描的記錄數.
reslen-返回結果集的大小.
nreturned-本次查詢實際返回的結果集.
update-代表這是一個update更新操做.
fastmod-Indicates a fast modify operation. See Updates. These operations are normally quite fast.
fastmodinsert – indicates a fast modify operation that performed an upsert.
upsert-代表update的upsert參數爲true.此參數的功能是若是update的記錄不存在,則用update的條件insert一條記錄.
moved-代表本次update是否移動了硬盤上的數據,若是新記錄比原記錄短,一般不會移動當前記錄,若是新記錄比原記錄長,那麼可能會移動記錄到其它位置,這時候會致使相關索引的更新.磁盤操做更多,加上索引更新,會使得這樣的操做比較慢.
insert-這是一個insert插入操做.
getmore-這是一個getmore 操做,getmore一般發生在結果集比較大的查詢時,第一個query返回了部分結果,後續的結果是經過getmore來獲取的。
Profiler 的效率
Profiling 功能確定是會影響效率的,可是不太嚴重,緣由是他使用的是system.profile 來記錄,而system.profile 是一個capped collection 這種collection 在操做上有一些限制和特色,可是效率更高。
示例:
#返回最近的10條記錄
db.system.profile.find().limit(10).sort({ ts : -1 }).pretty()
#返回全部的操做,除command類型的
db.system.profile.find( { op: { $ne : 'command' } } ).pretty()
#返回特定集合
db.system.profile.find( { ns : 'mydb.test' } ).pretty()
#返回大於5毫秒慢的操做
db.system.profile.find( { millis : { $gt : 5 } } ).pretty()
#從一個特定的時間範圍內返回信息
db.system.profile.find(
{
ts : {
$gt : new ISODate("2012-12-09T03:00:00Z") ,
$lt : new ISODate("2012-12-09T03:40:00Z")
}
}
).pretty()
#特定時間,限制用戶,按照消耗時間排序
db.system.profile.find(
{
ts : {
$gt : new ISODate("2011-07-12T03:00:00Z") ,
$lt : new ISODate("2011-07-12T03:40:00Z")
}
},
{ user : 0 }
).sort( { millis : -1 } )
mongodb啓用Profiling定位問題
建議使用方法三,最簡單且容易查看。
方法一:記錄全部日誌,查看mongod.log
若是mongodb已經運行了很長時間,此時查看mongod.log很大,無法打開查看相應信息
#ps -ef|grep mongod
找到相應的mongod的進程的pid
而後kill -SIGUSR1 pid 這樣mongod就會從新生成一個日誌文件
> db.setProfilingLevel(2);
{"was" : 0 , "ok" : 1}
> db.getProfilingLevel()
上面斜體的級別能夠取0,1,2 三個值,他們表示的意義以下:
0 – 不開啓
1 – 記錄慢命令 (默認爲>100ms)
2 – 記錄全部命令
開啓profiling後在mongod節點的log日誌中查看
cat mongod.log
或是tail -300 mongod.log
二.
之後大家遇到了這種某個操做慢的問題直接從數據庫裏在小數據下打開第5級log,看看它訪問了那些表格,直接去看Py中有沒有index
通常說來只要系統不癱瘓,沒有index是慢的直接緣由
db.adminCommand({logRotage:1})
kill -SIGUSR1 xxx
db.setLogLevel(1, "query")
db.setLogLevel(1, "command")
三
爲了之後儘快的定位專屬MongoDB的問題,請大家若是發現哪一個操做或者並行操做性能慢的話幫忙抓取一下MongoDB log。
分爲兩方面:
Profiling: 若是咱們定義全部的慢於100ms的數據庫操做多爲慢操做的話作以下操做:
use MyDomain
db.setProfilingLevel(1, 100), 用你測試的Domain 替換MyDomain
這樣全部的慢操做都會在 db.system.profile這裏顯示。
db.setProfilingLevel(2) 是打開全部操做profile, 能夠實驗用,或者短時打開。可是必定要及時關閉。
show collections 會看到多了一個collection 叫system.profile
而後db.system.profile.find().pretty()
Log: 用db.setLogLevel(1,command)能夠看到全部的操做,每一個操做的用時都會記錄下來。
記得定位後用db.setLogLevel(0,command)改回去。