Mongodb是一种单进程且灵活的非关系型数据库。但由于其软件小巧,单进程多线程的特点注定不会存在完善的系统动态性能视图,这点与mysql类似,但个人认为即便与mysql对比,不足还是显而易见。此外,对于运维人员来说,多数人习惯使用字符终端处理问题,由于mongodb是基于json,所以输出都是json格式,这点跟关系型数据库输出的行列格式相比,个人感觉可读性相对较差。所以,基于运维需要的MongDB监控能力补齐,是运维工作不可或缺工作之一,至于是脚本形式还是工具平台形式,都可以基于各自已有IT能力水平有计划分步骤的进行实施。毕竟不管白猫黑猫,能抓老鼠的就是好猫。
数据库性能数据获取诊断方式对比:
数据库 | 进程结构 | 获取、诊断性能问题的方式 | 备注 |
oracle | 多进程 | 1、有大量的动态性能视图,如v$session,v$latch等,可进行关联查询反馈当前数据库实例的会话等待、会话CPU、IO等资源消耗等 2、存在大量的历史视图或AWR、ASH等工具可分析历史性能问题 3、可以进行进程、会话、SQL语句的不同级别的跟踪或者SSD等进行诊断 4、由于是多进程结构,异常进程多数会生成trace文件,协助诊断问题 | |
mysql | 单进程多线程 | 1、存在少量多台性能视图可供查询 2、有独立的慢日志可获取执行慢或者开销较大的SQL语句 | |
mongodb | 单进程多线程 | 1、通过mongotop、mongostat工具诊断 2、存在少量动态性能集合或者数据库命令 3、慢查询日志整合在数据库日志中 |
mongodb性能监控详细介绍
在数据库日常运维过程中,数据库性能监控是重点工作之一。对于mongodb来说,由于在数据库中不会保存历史性能信息,所以我们只能通过使用集中监控软件或者自己编写脚本的方式采集性能数据保存在本地,本文只介绍利用Mongodb自带工具或者命令采集性能数据。
1、操作系统资源使用率监控
操作系统CPU、内存、IO等资源使用率监控,在任一数据库产品都需要部署,本文不再阐述。
2、mongotop&&mongostat
mongotop和mongostat是mongodb官方提供的两个在不同级别对数据库性能进行监控的工具,可以获取到实例全局和表级的性能数据。通过在操作系统上部署定时任务的方式执行这两个命令并将结果输出保存,可用于历史性能分析。
1)、mongotop
返回数据库集合级别的读写耗时情况,输出结果按照读写耗时降序排列,排序越靠前表示该集合访问并发越大或者说该集合上执行的MQL语句执行计划不是最优。输出结果示例如下:
2)、mongostat
mongostat类似操作系统的vmstat命令,返回实例全局每秒钟整体性能情况,包括读写次数、内存占用,锁队列、网络、连接数等信息。如果是集群,建议使用—discover参数获取集群全局信息。
oracle有一个监控利器叫Oswatcher,使用mongostat&&mongotop合理编写脚本,在用crontab定时调用,可实现类似oswatcher的功能,作为历史性能数据分析。
3、慢查询分析
mongodb的慢查询,可以通过设置profile后,在system.profile集合或者在数据库日志中获取。设置命令为db.setProfilingLevel(level,options),如db.setProfilingLevel(1,{ slowms: 200 }),表示级别为1,超过200ms的操作将被捕获,设置级别为0表示不捕获,级别为2表示捕获所有操作。可通过db.getProfilingStatus()命令查询当前的profile设置级别。
1)、system.profile
system.profile是通过使用db.setProfilingLevel(level,options)命令设置后在每一个库中生成的固定大小为1MB的集合,该集合根据设置的profile级别保存所有或者超过指定时间的操作命令信息,当保存数据过多超过1MB时,会自动清理历史数据。
保存信息示例:
{
"op": "query", --操作类型为查询
"ns": "opn###od.optN###em", --操作的库名.集合名
"command": {
"find": "optNu###tem", --集合名称
"filter": { --过滤条件
"_id": {
"res##id": "1000000000000003733049",
"res##etype": "3002"
}
},
"limit": 1, --limit条数
"singleBatch": true,
"$db": "opnu###od" --库名
},
"keysExamined": 0,
"docsExamined": 0,
"cursorExhausted": true,
"numYield": 0,
"locks": { ---本次操作的锁信息
"Global": {
"acquireCount": {
"r": NumberLong(2)
}
},
"Database": {
"acquireCount": {
"r": NumberLong(1)
}
},
"Collection": {
"acquireCount": {
"r": NumberLong(1)
}
}
},
"nreturned": 0,
"responseLength": 237,
"protocol": "op_query",
"millis": 77432, --操作耗时,单位为毫秒
"planSummary": "IDHACK", ---执行计划类型
"execStats": {
"stage": "IDHACK",
"nReturned": 0,
"executionTimeMillisEstimate": 0,
"works": 1,
"advanced": 0,
"needTime": 0,
"needYield": 0,
"saveState": 0,
"restoreState": 0,
"isEOF": 1,
"invalidates": 0,
"keysExamined": 0,
"docsExamined": 0
},
"ts": ISODate("2019-11-14T06:47:45.285Z"),--操作时间
"client": "10***227", ---客户端IP
"allUsers": [
{
"user": "user_admin",
"db": "admin"
}
],
"user": "user_admin@admin"
}
向上滑动查看更多内容
2)、从日志中获取慢查询
在mongodb的后台日志中会同时记录一份慢查询信息,相对于system.profile集合的优点是会永久保存,缺点是需要从海量的日志中过滤慢日志信息,不过若数据库的性能较差且并发较大,此时可看到日志上有大量的慢查寻日志刷屏,一般也可快速定位问题。
日志中输出的内容跟system.profile保存的基本一致,只是可读性稍微较差些,示例见上图。
4、当前会话操作获取
在mongodb中,如需获取当前的会话信息,可使用db.currentOp()命令,该命令获取到的信息,类似于在oracle中通过联合查询v$session和v$process获取到的信息,包含客户端类型、IP、正在执行的命令、正在操作的对象、执行计划、锁等等详细信息,其中包括活动会话和非活动会话信息。对于自己需要的信息,可通过相关的过滤条件进行过滤,如下示例:
--获取活动会话信息
db.currentOp(
{
"active": true
}
)
--查询所有在等待锁,且操作命令为增删改的会话信息。
db.currentOp(
{
"waitingForLock": true,
$or:[
{"op" : { "$in" : [ "insert", "update","remove" ] } },
{"query.findandmodify": { $exists: true } }
]
}
)
从3.6版本开始,提供了$currentOp这个聚合管道阶段,可以更加灵活的获取到当前的会话操作信息,并且返回的是游标,规避了db.currentOp的最大16MB的BSON大小限制。
--获取活动会话,
useadmin
db.aggregate([
{$currentOp: { allUsers: true ,idleConnections:false} },
{$project:{opid:1,client:1,op:1,ns:1,microsecs_running:1}},
{$match:{client:{$exists:true}}}
]);
针对获取到的异常会话或者执行时间长的会话,可以通过db.killOp()命令kill客户端会话。
常见性能问题处理
1、通过创建索引优化慢查询
跟其他关系型数据库一样,当出现对大量的慢查询时,可以检查其执行计划,若执行计划是全表扫描(COLLSCAN)语句时,可以通过在选择性较高的过滤条件上创建索引来优化查询。db.collection.createIndex({field:1},{background,true});
注:生产系统创建索引一定必须在后台创建索引,即指定{background,true},否则会产生库级锁
2、数据库阻塞处理
mongodb作为一款轻量级nosql数据库,在并发锁控制上相对弱些。部分操作会加库级锁,如前台创建索引、碎片整理、repair等,库级锁会阻塞当前数据库的所有操作,最明显的表现就是会导致数据库会话数逐渐上升,阻塞时间长还会导致连接数爆满。此类操作一般会记录在后台日志中,通过db.currentop()也可获取会话信息,然后通过db.killOp()命令kill会话。注:killop只能kill客户端会话,并且不是所有的操作都可以kill。
3、内存优化处理
在3.2版本以后,默认使用wiredtiger存储引擎,可以通过参数cacheSizeGB设置存储引擎的内存使用上限,默认是系统内存的60%。可以通过mongostat观察内存cache的使用情况,一般情况下used会小于80%,如果长时间的used达90%或者dirty10%,可考虑增加内存或者检查IO 。
4、数据库写入性能较差
写入性能差,需确定是整体性能差还是某个集合写入性能差,如果是整体都差的话,需检查IO,考虑更换性能更好的存储或者进行分片打散IO。如果是某个集合写入性能差,则需要检查这个集合是否索引过多,或者高并发或者异常语句导致异常加锁。
--查询在某个库中从未使用过的索引,如果索引长期未使用,建议删除
db.getCollectionNames().forEach(function(collection){
if(db[collection].getIndexes().length != 1){
print("Indexesfor "+ collection + ":");
db[collection].aggregate([{
$indexStats:{}
},{
$project:{
host:0
}
}]).forEach(
function(opDoc){
if(opDoc.accesses.ops== 0&& opDoc.name!= "_id_"){
printjson(opDoc);
}
});
}
});
向上滑动查看更多内容
5、便捷的数据生命周期管理
TTL索引是MongoDB中一种特殊的索引,可以支持文档在一定时间之后自动过期删除。可以再除_id外的所有字段上建立,但是只有在当字段值是时间或者数组中包含时间时,TTL索引才会执行删除操作,数组中包含多个时间时,按照数组中最小的时间计算。
例:db.collection.createIndex({ "InsertTime": 1 }, { expireAfterSeconds: 2592000} );
6、内存数据库引擎
Mongodb企业版拥有自己的内存数据库引擎,但是需要收费。在社区版中,可将数据文件(dbpath)放入/de/shm中,变相使用内存引擎。
注:由于内存一般不会太大,建议合理选择业务,并使用固定大小集合。