mongostat
mongostat是MongoDB自帶的監控工具,其可以提供數據庫節點或者整個集群當前的狀態視圖。該功能的設計非常類似于Linux系統中的vmstat命令,可以呈現出實時的狀態變化。不同的是,mongostat所監視的對象是數據庫進程。mongostat常用于查看當前的QPS/內存使用/連接數,以及多個分片的壓力分布。mongostat采用Go語言實現,其內部使用了db.serverStatus()命令,要求執行用戶需具備clusterMonitor角色權限。
mongostat -h 192.168.65.174 --port 28017 -ufox -pfox --authenticationDatabase=admin --discover -n 300 2
參數說明:
- -h:指定監聽的主機,分片集群模式下指定到一個mongos實例,也可以指定單個mongod,或者復制集的多個節點。
- --port:接入的端口,如果不提供則默認為27017。
- -u:接入用戶名,等同于-user。
- -p:接入密碼,等同于-password。
- --authenticationDatabase:鑒權數據庫。
- --discover:啟用自動發現,可展示集群中所有分片節點的狀態。
- -n 300 2:表示輸出300次,每次間隔2s。也可以不指定“-n 300”,此時會一直保持輸出。
指標說明
指標名 | 說明 |
inserts | 每秒插入數 |
query | 每秒查詢數 |
update | 每秒更新數 |
delete | 每秒刪除數 |
getmore | 每秒getmore數 |
command | 每秒命令數,涵蓋了內部的一些操作 |
%dirty | WiredTiger緩存中臟數據百分比 |
%used | WiredTiger 正在使用的緩存百分比 |
flushes | WiredTiger執行CheckPoint的次數 |
vsize | 虛擬內存使用量 |
res | 物理內存使用量 |
qrw | 客戶端讀寫等待隊列數量,高并發時,一般隊列值會升高 |
arw | 客戶端讀寫活躍個數 |
netIn | 網絡接收數據量 |
netOut | 網絡發送數據量 |
conn | 當前連接數 |
set | 所屬復制集名稱 |
repl | 復制節點狀態(主節點/二級節點……) |
time | 時間戳 |
mongostat需要關注的指標主要有如下幾個:
- 插入、刪除、修改、查詢的速率是否產生較大波動,是否超出預期。
- qrw、arw:隊列是否較高,若長時間大于0則說明此時讀寫速度較慢。
- conn:連接數是否太多。
- dirty:百分比是否較高,若持續高于10%則說明磁盤I/O存在瓶頸。
- netIn、netOut:是否超過網絡帶寬閾值。
- repl:狀態是否異常,如PRI、SEC、RTR為正常,若出現REC等異常值則需要修復。
使用交互模式
mongostat一般采用滾動式輸出,即每一個間隔后的狀態數據會被追加到控制臺中。從MongoDB 3.4開始增加了--interactive選項,用來實現非滾動式的監視,非常方便。
mongostat -h 192.168.65.174 --port 28017 -ufox -pfox --authenticationDatabase=admin --discover --interactive -n 2
mongotop
mongotop命令可用于查看數據庫的熱點表,通過觀察mongotop的輸出,可以判定是哪些集合占用了大部分讀寫時間。mongotop與mongostat的實現原理類似,同樣需要clusterMonitor角色權限。
mongotop -h 192.168.65.174 --port=28017 -ufox -pfox --authenticationDatabase=admin
默認情況下,mongotop會持續地每秒輸出當前的熱點表
指標說明
指標名 | 說明 |
ns | 集合名稱空間 |
total | 花費在該集合上的時長 |
read | 花費在該集合上的讀操作時長 |
write | 花費在該集合上的寫操作時長 |
mongotop通常需要關注的因素主要包括:
- 熱點表操作耗費時長是否過高。這里的時長是在一定的時間間隔內的統計值,它代表某個集合讀寫操作所耗費的時間總量。在業務高峰期時,核心表的讀寫操作一般比平時高一些,通過mongotop的輸出可以對業務尖峰做出一些判斷。
- 是否存在非預期的熱點表。一些慢操作導致的性能問題可以從mongotop的結果中體現出來
mongotop的統計周期、輸出總量都是可以設定的
#最多輸出100次,每次間隔時間為2smongotop -h 192.168.65.174 --port=28017 -ufox -pfox --authenticationDatabase=admin -n 100 2
Profiler模塊
Profiler模塊可以用來記錄、分析MongoDB的詳細操作日志。默認情況下該功能是關閉的,對某個業務庫開啟Profiler模塊之后,符合條件的慢操作日志會被寫入該庫的system.profile集合中。Profiler的設計很像代碼的日志功能,其提供了幾種調試級別:
級別 | 說明 |
0 | 日志關閉,無任何輸出 |
1 | 部分開啟,僅符合條件(時長大于slowms)的操作日志會被記錄 |
2 | 日志全開,所有的操作日志都被記錄 |
對當前的數據庫開啟Profiler模塊:
# 將level設置為2,此時所有的操作會被記錄下來。db.setProfilingLevel(2)#檢查是否生效db.getProfilingStatus()
- slowms是慢操作的閾值,單位是毫秒;
- sampleRate表示日志隨機采樣的比例,1.0則表示滿足條件的全部輸出。
如果希望只記錄時長超過500ms的操作,則可以將level設置為1
db.setProfilingLevel(1,500)
還可以進一步設置隨機采樣的比例
db.setProfilingLevel(1,{slowms:500,sampleRate:0.5})
查看操作日志
開啟Profiler模塊之后,可以通過system.profile集合查看最近發生的操作日志
db.system.profile.find().limit(5).sort({ts:-1}).pretty()
這里需要關注的一些字段主要如下所示:
- op:操作類型,描述增加、刪除、修改、查詢。
- ns:名稱空間,格式為{db}.{collection}。
- Command:原始的命令文檔。
- Cursorid:游標ID。
- numYield:操作數,大于0表示等待鎖或者是磁盤I/O操作。
- nreturned:返回條目數。
- keysExamined:掃描索引條目數,如果比nreturned大出很多,則說明查詢效率不高。docsExamined:掃描文檔條目數,如果比nreturned大出很多,則說明查詢效率不高。
- locks:鎖占用的情況。
- storage:存儲引擎層的執行信息。
- responseLength:響應數據大小(字節數),一次性查詢太多的數據會影響性能,可以使用limit、batchSize進行一些限制。
- millis:命令執行的時長,單位是毫秒。
- planSummary:查詢計劃的概要,如IXSCAN表示使用了索引掃描。
- execStats:執行過程統計信息。
- ts:命令執行的時間點。
根據這些字段,可以執行一些不同維度的查詢。比如查看執行時長最大的10條操作記錄
查看某個集合中的update操作日志
db.system.profile.find().limit(10).sort({millis:-1}).pretty()
查看某個集合中的update操作日志
db.system.profile.find({op:"update",ns:"shop.user"})
注意事項
- system.profile是一個1MB的固定大小的集合,隨著記錄日志的增多,一些舊的記錄會被滾動刪除。
- 在線上開啟Profiler模塊需要非常謹慎,這是因為其對MongoDB的性能影響比較大。建議按需部分開啟,同時slowms的值不要設置太低。
- sampleRate的默認值是1.0,該字段可以控制記錄日志的命令數比例,但只有在MongoDB 4.0版本之后才支持。
- Profiler模塊的設置是內存級的,重啟服務器后會自動恢復默認狀態。
db.currentOp()
Profiler模塊所記錄的日志都是已經發生的事情,db.currentOp()命令則與此相反,它可以用來查看數據庫當前正在執行的一些操作。想象一下,當數據庫系統的CPU發生驟增時,我們最想做的無非是快速找到問題的根源,這時db.currentOp就派上用場了。
db.currentOp()讀取的是當前數據庫的命令快照,該命令可以返回許多有用的信息,比如:
- 操作的運行時長,快速發現耗時漫長的低效掃描操作。
- 執行計劃信息,用于判斷是否命中了索引,或者存在鎖沖突的情況。
- 操作ID、時間、客戶端等信息,方便定位出產生慢操作的源頭。
對示例操作的解讀如下:
(1)從ns、op字段獲知,當前進行的操作正在對test.items集合執行update命令。
(2)command字段顯示了其原始信息。其中,command.q和command.u分別展示了update的查詢條件和更新操作。
(3)"planSummary":"COLLSCAN" 說明情況并不樂觀,update沒有利用索引而是正在全表掃描。(4)microsecs_running:NumberLong(186070)表示操作運行了186ms,注意這里的單位是微秒。
優化方向:
- value字段加上索引
- 如果更新的數據集非常大,要避免大范圍update操作,切分成小批量的操作
opid表示當前操作在數據庫進程中的唯一編號。如果已經發現該操作正在導致數據庫系統響應緩慢,則可以考慮將其“殺”死
db.killOp(4001)
db.currentOp默認輸出當前系統中全部活躍的操作,由于返回的結果較多,我們可以指定一些過濾條件:
- 查看等待鎖的增加、刪除、修改、查詢操作
db.currentOp({waitingForLock:true,$or:[{op:{$in:["insert","update","remove"]}},{"query.findandmodify":{$exists:true}}]})
- 查看執行時間超過1s的操作
db.currentOp({secs_running:{$gt:1}})查看test數據庫中的操作
db.currentOp({ns:/test/})
currentOp命令輸出說明
- currentOp.type:操作類型,可以是op、idleSession、idleCursor的一種,一般的操作信息以op表示。其為MongoDB 4.2版本新增功能。
- currentOp.host:主機的名稱。currentOp.desc:連接描述,包含connectionId。currentOp.connectionId:客戶端連接的標識符。currentOp.client:客戶端主機和端口。currentOp.appName:應用名稱,一般是描述客戶端類型。
- currentOp.clientMetadata:關于客戶端的附加信息,可以包含驅動的版本。currentOp.currentOpTime:操作的開始時間。MongoDB 3.6版本新增功能。
- currentOp.lsid:會話標識符。MongoDB 3.6版本新增功能。
- currentOp.opid:操作的標志編號。
- currentOp.active:操作是否活躍。如果是空閑狀態則為false。
- currentOp.secs_running:操作持續時間(以秒為單位)。
- currentOp.microsecs_running:操作持續時間(以微秒為單位)。
- currentOp.op:標識操作類型的字符串。可能的值是:"none" "update" "insert""query""command" "getmore" "remove" "killcursors"。其中,command操作包括大多數命令,如createIndexes和findAndModify。
- currentOp.ns:操作目標的集合命名空間。
- currentOp.command:操作的完整命令對象的文檔。如果文檔大小超過1KB,則會使用一種$truncate形式表示。
- currentOp.planSummary:查詢計劃的概要信息。
- currentOp.locks:當前操作持有鎖的類型和模式。
- currentOp.waitingForLock:是否正在等待鎖。
- currentOp.numYields:當前操作執行yield(讓步)的次數。一些鎖互斥或者磁盤I/O讀取都會導致該值大于0。
- currentOp.lockStats:當前操作持有鎖的統計。
- currentOp.lockStats.acquireCount:操作以指定模式獲取鎖的次數。
- currentOp.lockStats.acquireWaitCount:操作獲取鎖等待的次數,等待是因為鎖處于沖突模式。acquireWaitCount小于或等于acquireCount。
- currentOp.lockStats.timeAcquiringMicros:操作為了獲取鎖所花費的累積時間(以微秒為單位)。timeAcquiringMicros除以acquireWaitCount可估算出平均鎖等待時間。
- currentOp.lockStats.deadlockCount:在等待鎖獲取時,操作遇到死鎖的次數。
注意事項
- db.currentOp返回的是數據庫命令的瞬時狀態,因此,如果數據庫壓力不大,則通常只會返回極少的結果。
- 如果啟用了復制集,那么currentOp還會返回一些復制的內部操作(針對local.oplog.rs),需要做一些篩選。
- db.currentOp的結果是一個BSON文檔,如果大小超過16MB,則會被壓縮。可以使用聚合操作$currentOp獲得完整的結果。