Using mongoDB's Profiler analyze the performance of database operations

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介:
慢查询在数据库分析中是一个非常重要的参考。
我记得在PostgreSQL Conference 2009 中曾经就有人专门讲解了Hit the mole.(90%的数据库性能问题往往出现在10%的SQL上面.)
在MySQL,PostgreSQL数据库中,用日志来记录慢查询。(比如为数据库设置一下阀值100MS,运行时间超过100MS的SQL将记录到日志中.)
在mongoDB中也比较类似,使用profile 来配置慢查询的阀值.已经是否开启记录慢查询的功能。mongoDB的慢查询被记录到system.profile collection中。
在mongoDB  1.7.x 以下版本中使用getProfilingLevel()查看当前配置的数据库profile.1.7.x以后改为getProfilingStatus().
配置profile可以通过参数文件,或在mongo shell中执行命令来配置.
1. 参数文件
  --profile arg         0=off 1=slow, 2=all  (0表示关闭profile,1表示只记录执行时间超过slowms配置的值的执行内容,2表示记录所有执行内容)
  --slowms arg (=100)   value of slow for profile and console log (如果profile配置为1并且没有配置slowms的话默认是100毫秒)
2. mongo shell
    db.setProfilingLevel( level , slowms ) 
3. 查看profile
> db.getProfilingLevel()
1

配置举例:
# 配置slowms=1,profile=1
> db.setProfilingLevel(1,1)  
{ "was" : 0, "slowms" : 100, "ok" : 1 }

# 注意这里返回的slowms : 100,实际上已经配置为1了,估计是个BUG。在1.7.x里面可以通过以下命令查看:
> db.getProfilingStatus()
{ "was" : 1, "slowms" : 1 }

# 插入测试数据
> for (var i=0;i<10000;i++) {
... db.userinfo.insert({"firstname" : "zhou","lastname" : "digoal" + i,"age" : i})
... }

# 在1.6.5的环境下,没有办法通过db.getProfilingLevel()查看slowms的具体配置,不过通过以下测试可以知道,前面配置的slowms=1已经生效.
> db.userinfo.find({"firstname" : "zhou"}).explain()
{
"cursor" : "BasicCursor",
"nscanned" : 10000,
"nscannedObjects" : 10000,
"n" : 10000,
"millis" : 5,
"indexBounds" : {

}
}
> db.system.profile.find()
{ "ts" : "Sun Jan 09 2011 09:02:56 GMT+0800 (CST)", "info" : "insert admin.userinfo", "millis" : 1 }
{ "ts" : "Sun Jan 09 2011 09:07:21 GMT+0800 (CST)", "info" : "query admin.userinfo reslen:202 nscanned:10000 \nquery: { query: { firstname: \"zhou\" }, $explain: true } nreturned:1 5ms", "millis" : 5 }

# 使用以下方式查看效果也是一样的
> db.system.profile.find( function() { return this.info.indexOf('$cmd')<0; } )
{ "ts" : "Sun Jan 09 2011 09:02:56 GMT+0800 (CST)", "info" : "insert admin.userinfo", "millis" : 1 }
{ "ts" : "Sun Jan 09 2011 09:07:21 GMT+0800 (CST)", "info" : "query admin.userinfo reslen:202 nscanned:10000 \nquery: { query: { firstname: \"zhou\" }, $explain: true } nreturned:1 5ms", "millis" : 5 }
# 也可以使用规则表达式过滤出你关心的collection,如下.
> db.system.profile.find({"info" : /userinfo/})   
{ "ts" : "Sun Jan 09 2011 09:02:56 GMT+0800 (CST)", "info" : "insert admin.userinfo", "millis" : 1 }
{ "ts" : "Sun Jan 09 2011 09:07:21 GMT+0800 (CST)", "info" : "query admin.userinfo reslen:202 nscanned:10000  \nquery: { query: { firstname: \"zhou\" }, $explain: true }  nreturned:1 5ms", "millis" : 5 }

# 或者按millis排个序
> db.system.profile.find().sort({"millis" : -1})
{ "ts" : "Sun Jan 09 2011 09:07:21 GMT+0800 (CST)", "info" : "query admin.userinfo reslen:202 nscanned:10000  \nquery: { query: { firstname: \"zhou\" }, $explain: true }  nreturned:1 5ms", "millis" : 5 }
{ "ts" : "Sun Jan 09 2011 09:02:56 GMT+0800 (CST)", "info" : "insert admin.userinfo", "millis" : 1 }

# 由于system.profile是capped collection,使用$natural  可以按照记录插入的插入顺序查看结果.
> db.system.profile.find().sort({"$natural" : -1})   
{ "ts" : "Sun Jan 09 2011 09:07:21 GMT+0800 (CST)", "info" : "query admin.userinfo reslen:202 nscanned:10000  \nquery: { query: { firstname: \"zhou\" }, $explain: true }  nreturned:1 5ms", "millis" : 5 }
{ "ts" : "Sun Jan 09 2011 09:02:56 GMT+0800 (CST)", "info" : "insert admin.userinfo", "millis" : 1 }

# 使用show profile 查看最近5%的超过1ms执行时间的事件
> show profile

5ms Sun Jan 09 2011 09:07:21
query admin.userinfo reslen:202 nscanned:10000   
query: { query: { firstname: "zhou" }, $explain: true }  nreturned:1 5ms


1ms Sun Jan 09 2011 09:02:56
insert admin.userinfo

输出格式:
1. ts : 记录下捕获的时间戳,(SQL执行结束时间,如下)
> Date()
Sun Jan 09 2011 09:42:31 GMT+0800 (CST)
> db.userinfo.ensureIndex({"lastname" : 1,"detail.city" : 1})
> db.system.profile.find().sort({"$natural" : -1}).limit(1)
{ "ts" : "Sun Jan 09 2011 09:42:46 GMT+0800 (CST)", "info" : "insert admin.system.indexes 14374ms", "millis" : 14374 }
# 从时间上看是执行结束时间.
2.  millis : 执行时间,注意不包含lock请求时间和network传输时间,只包含了server处理这条请求的时间.
3. info : 执行详细信息,包含(query,update,insert,getmore)
        query : 数据库查询操作,包含以下信息:
                ntoreturn : Number of objects the client requested for return from a query. For example, <code>findOne()</code> sets ntoreturn to 1.<code>limit()</code> sets the appropriate limit. Zero indicates no limit.
                query : Details of the query spec.
                nscanned : Number of objects scanned in executing the operation.
                reslen : Query result length in bytes.
                nreturned : Number of objects returned from query.
         update : A database update operation. <code>save()</code> calls generate either an update or insert operation.
                  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 : Indicates on upsert performed.
                  moved : Indicates the update moved the object on disk (not updated in place). This is slower than an in place update, and normally occurs when an object grows.
          insert : A database insert.
          getmore : For large queries, the database initially returns partial information. getmore indicates a call to retrieve further information.
# 例:
> db.userinfo.find().sort({"age" : -1}).limit(1)      
{ "_id" : ObjectId("4d2910734f7371f2497bd6ad"), "firstname" : "zhou", "lastname" : "digoal999998", "age" : 999998, "detail" : { "city" : "HangZhou", "corp" : "sky-mobi" } }
> db.system.profile.find().sort({"$natural" : -1}).limit(1)
{ "ts" : "Sun Jan 09 2011 09:53:42 GMT+0800 (CST)", "info" : "query admin.userinfo ntoreturn:1 scanAndOrder  reslen:169 nscanned:2009998  \nquery: { query: {}, orderby: { age: -1.0 } }  nreturned:1 3140ms", "millis" : 3140 }
# 性能优化相关
从上面的profile结果看出ntoreturn = 1,nscanned = 2009998 ,也就是说返回结果是1条,但是扫描了200多W条记录。如果这种查询非常多的话建议在"age"上建立索引。对于update操作同样适用.(当然,还要考虑索引对写入带来的overhead)
如果reslen这个值比较大,表示返回结果非常多,建议调整find()限制输出结果.

调整profileSIZE:
1. 关闭profile
> db.setProfilingLevel(0)
{ "was" : 1, "slowms" : 1, "ok" : 1 }
2. 调整profile size
> db.system.profile.renameCollection("system.oldprofile")
{ "ok" : 1 }
> show collections                                        
system.indexes
system.oldprofile
system.users
userinfo
version
# 调整为100MB
> db.createCollection("system.profile",{"capped" : true, "size" : 102400000})
{ "ok" : 1 }
> db.system.profile.stats()                                                   
{
        "ns" : "admin.system.profile",
        "count" : 0,
        "size" : 0,
        "avgObjSize" : NaN,
        "storageSize" : 102400256,
        "numExtents" : 1,
        "nindexes" : 0,
        "lastExtentSize" : 102400256,
        "paddingFactor" : 1,
        "flags" : 0,
        "totalIndexSize" : 0,
        "indexSizes" : {

        },
        "capped" : 1,
        "max" : 2147483647,
        "ok" : 1
}
3. 重新开启profile
> db.setProfilingLevel(1,1)
{ "was" : 0, "slowms" : 1, "ok" : 1 }
4. oldprofile中的数据无法插入到profile
测试:
> for (var c = db.system.oldprofile.find(); c.hasNext();) {
... b = c.next();
... db.system.profile.insert({"ts" : c.ts,"info" : c.info,"millis" : c.millis});  
... }
> db.system.profile.count()
0
# 没有插进去
# 非常抱歉,插入到test是OK的. system.profile 不可以手工插入记录
> for (var c = db.system.oldprofile.find(); c.hasNext();) {               
... b = c.next();
... db.test.insert({"ts" : c.ts,"info" : c.info,"millis" : c.millis});           
... }
> db.test.count()
6

5. 删除oldprofile
> db.system.oldprofile.drop()
Sun Jan  9 10:38:57 uncaught exception: drop failed: {
        "ns" : "admin.system.oldprofile",
        "errmsg" : "exception: can't drop system ns",
        "code" : 12502,
        "ok" : 0
}
# system下面的collection不允许删除,所以要先rename一下.
> db.system.oldprofile.renameCollection("oldprofile")
{ "ok" : 1 }
> db.oldprofile.drop()
true

注意事项:
1. system.profile 是capped collection,默认配置比较小,所以如果需要放更多的内容需要调整size和max (具体的话可以参考我以前写过的capped collection topic)
如:
> db.system.profile.stats()  
{
        "ns" : "admin.system.profile",
        "count" : 1,
        "size" : 68,
        "avgObjSize" : 68,
        "storageSize" : 131328,
        "numExtents" : 1,
        "nindexes" : 0,
        "lastExtentSize" : 131328,
        "paddingFactor" : 1,
        "flags" : 0,
        "totalIndexSize" : 0,
        "indexSizes" : {

        },
        "capped" : 1,
        "max" : 2147483647,
        "ok" : 1
}
> db.system.profile.totalSize()
131328

2. 开启profile对性能略有影响.

3. 通过命令行开启profile只对当前数据库生效,并且重启数据库后失效.
# 例如在admin库中使用mongo shell开启了profile,转到test库后是没有开启的.
> use test
switched to db test
> show collections
system.indexes
system.users
userinfo
> db.getProfilingLevel()
0
> db.setProfilingLevel(1,1)
{ "was" : 0, "slowms" : 1, "ok" : 1 }
> db.getProfilingLevel()   
1
# 使用mongo shell开启的profile重启后失效,如下
# 重启mongoDB
[root@db5 ~]# /opt/mongodb/bin/mongo 127.0.0.1:5281/admin
MongoDB shell version: 1.6.5
connecting to: 127.0.0.1:5281/admin
> db.auth("digoal","DIGOAL")
1
> db.getProfilingLevel()
0
# 显然system.profile是还在的.
> show collections
system.indexes
system.profile
system.users
userinfo
version
> db.system.profile.find()
{ "ts" : "Sun Jan 09 2011 09:02:56 GMT+0800 (CST)", "info" : "insert admin.userinfo", "millis" : 1 }
{ "ts" : "Sun Jan 09 2011 09:07:21 GMT+0800 (CST)", "info" : "query admin.userinfo reslen:202 nscanned:10000  \nquery: { query: { firstname: \"zhou\" }, $explain: true }  nreturned:1 5ms", "millis" : 5 }

4. system.profile 不可以手工插入记录
相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
3月前
|
JSON NoSQL 小程序
Mongodb数据库的导出和导入总结
Mongodb数据库的导出和导入总结
191 0
|
3月前
|
存储 NoSQL 大数据
mongodb数据库的优缺点
MongoDB 是一个流行的 NoSQL 数据库,主要用于大规模数据存储和处理。下面是 MongoDB 数据库的一些优点和缺点: ### 优点: 1. **灵活的模式设计**:MongoDB 是一个文档数据库,支持动态的模式设计,允许您存储不同结构和格式的数据。 2. **水平扩展**:MongoDB 支持水平扩展,可以轻松地在多个节点和服务器之间分布数据,以满足高并发和大规模数据处理的需求。 3. **丰富的查询功能**:MongoDB 提供了强大的查询语言和索引支持,允许您在大数据集上进行高效的数据检索和分析。 4. **高性能**:通过使用内存映射(mmap)和其他优化技术,M
316 0
|
2天前
|
NoSQL MongoDB 数据库
MongoDB数据恢复—MongoDB数据库文件被破坏的数据恢复案例
服务器数据恢复环境: 一台Windows Server操作系统服务器,服务器上部署MongoDB数据库。 MongoDB数据库故障&检测: 工作人员在未关闭MongoDB数据库服务的情况下,将数据库文件拷贝到其他分区。拷贝完成后将原MongoDB数据库所在分区进行了格式化操作,然后将数据库文件拷回原分区,重新启动MongoDB服务,服务无法启动。
|
5天前
|
NoSQL MongoDB Redis
Python与NoSQL数据库(MongoDB、Redis等)面试问答
【4月更文挑战第16天】本文探讨了Python与NoSQL数据库(如MongoDB、Redis)在面试中的常见问题,包括连接与操作数据库、错误处理、高级特性和缓存策略。重点介绍了使用`pymongo`和`redis`库进行CRUD操作、异常捕获以及数据一致性管理。通过理解这些问题、易错点及避免策略,并结合代码示例,开发者能在面试中展现其技术实力和实践经验。
122 8
Python与NoSQL数据库(MongoDB、Redis等)面试问答
|
1月前
|
NoSQL 网络协议 MongoDB
Windows公网远程连接MongoDB数据库【无公网IP】
Windows公网远程连接MongoDB数据库【无公网IP】
|
1月前
|
存储 NoSQL 关系型数据库
一篇文章带你搞懂非关系型数据库MongoDB
一篇文章带你搞懂非关系型数据库MongoDB
57 0
|
1月前
|
人工智能 NoSQL MongoDB
|
2月前
|
SQL NoSQL Java
文档型数据库MongoDB
文档型数据库MongoDB
|
2月前
|
JSON NoSQL MongoDB
MongoDB详解(五)——MongoDB数据库简单使用
MongoDB详解(五)——MongoDB数据库简单使用
106 1
|
2月前
|
存储 NoSQL Linux
MongoDB详解(四)——MongoDB数据库安装
MongoDB详解(四)——MongoDB数据库安装
67 2