MongoDB慢日志分析

MongoDB的数据库Profiler提供慢查询记录,类似MySQL的慢查询日志。可以通过设置--profile级别开启,记录慢查询时间默认为100ms。查询Profiler记录位于system.profile集合,可以通过db.system.profile.find()获取。优化建议包括减少nscanned与nreturned的差距,合理使用索引,以及在适用情况下使用fast modify operations。

在 MySQL中,慢查询日志是经常作为我们优化数据库的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是Mongo Database Profiler.不仅有,而且还有一些比MySQL的Slow Query Log更详细的信息。它就是我们这篇文章的主题。

  开启 Profiling 功能

  有两种方式可以控制 Profiling 的开关和级别,第一种是直接在启动参数里直接进行设置。

  启动MongoDB时加上–profile=级别 即可。

  也可以在客户端调用db.setProfilingLevel(级别) 命令来实时配置。可以通过db.getProfilingLevel()命令来获取当前的Profile级别。

> db.setProfilingLevel(2); 
  {"was" : 0 , "ok" : 1} 
  > db.getProfilingLevel()

  上面斜体的级别可以取0,1,2 三个值,他们表示的意义如下:

  0 – 不开启

  1 – 记录慢命令 (默认为>100ms)

  2 – 记录所有命令

  Profile 记录在级别1时会记录慢命令,那么这个慢的定义是什么?上面我们说到其默认为100ms,当然有默认就有设置,其设置方法和级别一样有两种,一种是通过添加–slowms启动参数配置。第二种是调用db.setProfilingLevel时加上第二个参数:

db.setProfilingLevel( level , slowms ) 
  db.setProfilingLevel( 1 , 10 );

  查询 Profiling 记录

  与MySQL的慢查询日志不同,Mongo Profile 记录是直接存在系统db里的,记录位置 system.profile ,所以,我们只要查询这个Collection的记录就可以获取到我们的 Profile 记录了。

> 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来获取的。

  MongoDB 查询优化

  如果nscanned(扫描的记录数)远大于nreturned(返回结果的记录数)的话,那么我们就要考虑通过加索引来优化记录定位了。

  reslen 如果过大,那么说明我们返回的结果集太大了,这时请查看find函数的第二个参数是否只写上了你需要的属性名。(类似 于MySQL中不要总是select *)

  对于创建索引的建议是:如果很少读,那么尽量不要添加索引,因为索引越多,写操作会越慢。如果读量很大,那么创建索引还是比较划算的。(和RDBMS一样,貌似是废话 -_-!!)

  MongoDB 更新优化

  如果写查询量或者update量过大的话,多加索引是会有好处的。以及~~~~(省略N字,和RDBMS差不多的道理)

  Use fast modify operations when possible (and usually with these, an index). See Updates.

  Profiler 的效率

  Profiling 功能肯定是会影响效率的,但是不太严重,原因是他使用的是system.profile 来记录,而system.profile 是一个capped collection 这种collection 在操作上有一些限制和特点,但是效率更高。

http://blog.zol.com.cn/3044/article_3043984.html

<think>我们参考了关于MongoDB慢查询日志分析的引用内容,特别是引用[3]中提到的慢查询分析流程和开启Profiling的方法。根据用户需求,我们将详细解释如何分析MongoDB慢查询日志。##分析MongoDB慢查询日志的方法MongoDB使用Profiling功能来记录慢查询日志,记录的信息存储在`system.profile`集合中。以下是具体步骤:###1.开启Profiling功能Profiling有三个级别:-**0**:关闭,不收集数据(默认)-**1**:收集慢查询数据(默认阈值100毫秒)-**2**:收集所有操作的数据**开启方法**(在MongoDBshell中执行):```javascript//设置Profiling级别为1(记录慢查询),并指定慢查询阈值为100毫秒(可根据需要调整)db.setProfilingLevel(1,{slowms:100})//查看当前Profiling设置db.getProfilingStatus()```###2.查询慢日志记录慢查询日志存储在`system.profile`集合中(每个数据库独立):```javascript//切换到目标数据库useyour_database//查询所有慢日志(按执行时间倒序)db.system.profile.find().sort({millis:-1}).pretty()//查询超过200毫秒的操作db.system.profile.find({millis:{$gt:200}}).pretty()```###3.分析关键字段日志记录的主要字段包括:-`op`:操作类型(如`query`,`insert`,`update`,`delete`)-`ns`:操作的命名空间(数据库.集合)-`millis`:执行时间(毫秒)-`ts`:操作执行的时间戳-`client`:客户端IP-`query`:查询条件-`planSummary`:执行计划摘要###4.使用`explain()`分析具体查询定位到慢查询后,用`explain()`分析其执行计划:```javascriptdb.collection.find({...}).explain("executionStats")```重点关注:-`executionStats.executionTimeMillis`:实际执行时间-`executionStats.totalKeysExamined`:索引扫描条目数-`executionStats.totalDocsExamined`:文档扫描数-`stage`:查询执行阶段(如`COLLSCAN`表示全表扫描)###5.优化措施-**添加索引**:如果出现`COLLSCAN`(全表扫描),为查询字段创建索引-**优化查询语句**:避免使用`$where`、正则表达式开头模糊匹配(如`/^abc/`)-**调整慢查询阈值**:根据业务需求调整`slowms`值-**限制返回字段**:减少网络传输和数据解析开销###示例分析假设日志中有一条慢查询:```json{"op":"query","ns":"test.orders","query":{"status":"pending","amount":{"$gt":1000}},"millis":350,"planSummary":"COLLSCAN"}```分析步骤:1.该查询耗时350ms,在`orders`集合上执行2.执行计划为`COLLSCAN`(全表扫描),说明缺少索引3.创建复合索引解决:```javascriptdb.orders.createIndex({status:1,amount:1})```##注意事项-`system.profile`是固定集合(cappedcollection),默认大小1MB。调整大小:```javascriptdb.createCollection("system.profile",{capped:true,size:1024*1024*10})//10MB```-生产环境避免开启Level2(记录所有操作),否则可能影响性能[^3]。§§相关问题§§1.如何动态调整MongoDB慢查询的阈值?2.如何解读`explain("executionStats")`的输出结果?3.MongoDB索引有哪些类型?分别适用于什么场景?4.如何监控MongoDB的慢查询日志实时变化?[^1]:[MongoDB官方Profiling文档](http://docs.mongodb.org/manual/tutorial/manage-the-database-profiler/)[^2]:[MongoDB慢查询优化实践](https://www.example.com/mongodb-slow-query-optimization)(示例链接)[^3]:引用内容3:mongodbProfiling通过慢查询日志分析查询慢的原因</think>### 如何分析MongoDB慢查询日志 分析MongoDB慢查询日志主要通过**Profiling功能**实现,以下是完整操作流程: --- #### 1. **开启慢查询日志记录** ```javascript // 设置Profiling级别(在目标数据库执行) use your_database db.setProfilingLevel(1, { slowms: 100 }) // 级别1+100ms阈值 // 验证设置 db.getProfilingStatus() ``` - **级别说明**: - `0`:关闭分析 - `1`:记录慢操作(默认阈值100ms) - `2`:记录所有操作(生产环境慎用)[^3] --- #### 2. **查询慢日志内容** ```javascript // 查看所有慢查询(按耗时倒序) db.system.profile.find().sort({ millis: -1 }).pretty() // 筛选特定操作(如超过200ms的查询) db.system.profile.find({ millis: { $gt: 200 }, op: "query" }).pretty() ``` --- #### 3. **关键字段解析** 日志文档核心字段: | 字段 | 说明 | |-------------|----------------------------------------------------------------------| | `op` | 操作类型(query/insert/update/delete) | | `ns` | 命名空间(database.collection) | | `millis` | **执行耗时(毫秒)** ⭐重点关注 | | `query` | 查询条件(JSON格式) | | `planSummary` | 执行计划(如`COLLSCAN`表示全表扫描) | | `client` | 客户端IP | | `ts` | 时间戳(ISO格式) | 示例日志片段: ```json { "op": "query", "ns": "shop.orders", "query": { "status": "pending", "amount": { "$gt": 1000 } }, "millis": 350, "planSummary": "COLLSCAN" // 全表扫描警告! } ``` --- #### 4. **深度性能分析** 对可疑查询使用`explain()`: ```javascript db.orders.find( { status: "pending", amount: { $gt: 1000 } } ).explain("executionStats") ``` 关注结果中的: - `executionStats.executionTimeMillis`:实际执行时间 - `executionStats.totalKeysExamined`:索引扫描条目数 - `executionStats.totalDocsExamined`:文档扫描数 - `stage`:出现`COLLSCAN`需紧急优化 --- #### 5. **优化措施** 根据日志分析结果: 1. **添加缺失索引**(针对频繁全表扫描字段) ```javascript db.orders.createIndex({ status: 1, amount: 1 }) ``` 2. **重构查询**:避免`$where`/正则表达式等低效操作 3. **调整阈值**:动态设置合理慢查询阈值 ```javascript db.setProfilingLevel(1, { slowms: 50 }) // 调低至50ms ``` 4. **日志维护**:定期清理或扩容`system.profile`集合 ```javascript db.createCollection("system.profile", { capped: true, size: 10000000 // 10MB容量 }) ``` > **注意**:生产环境避免长期开启Level 2分析,可能引发性能损耗[^3]。 --- #### 6. **自动化工具推荐** - **mtools**:日志分析工具集(支持聚合/可视化) ```bash mlogfilter mongod.log --slow --json | mplotqueries ``` - **MongoDB Atlas**:云服务内置性能分析面板 - **Percona PMM**:开源监控方案
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值