MongoDB慢日志查询及索引创建

MongoDB 的慢日志(Slow Query Log)对于运维和程序员来说都非常重要,因为它直接关系到数据库的性能和应用程序的稳定性。以下分享介绍下MongoDB慢日志查询及索引创建相关的一些笔记。

一,准备

1. 使用 db.currentOp() 实时监控

db.currentOp() 可以查看当前正在执行的操作,适合捕捉瞬时的高 CPU 操作。

javascript 复制代码
db.currentOp()

示例:过滤长时间运行的操作

javascript 复制代码
db.currentOp({ "secs_running": { "$gt": 1 } }) // 过滤运行时间超过 1 秒的操作

关键字段:

  • opid:操作 ID。

  • op:操作类型(如 queryupdatecommand 等)。

  • ns:操作的命名空间(数据库和集合)。

  • secs_running:操作已经运行的时间(秒)。

  • planSummary:操作的执行计划摘要。

  • locks:操作持有的锁信息。

2. 查询当前 profiling 状态

运行以下命令:

javascript 复制代码
db.getProfilingStatus()

输出示例:

javascript 复制代码
{
  "was": 1,
  "slowms": 100,
  "sampleRate": 1.0
}
  • was:当前的 profiling 级别。

    • 0:关闭 profiling。

    • 1:记录慢查询。

    • 2:记录所有查询。

  • slowms:当前的慢查询阈值(单位:毫秒)。

  • sampleRate:采样率(MongoDB 4.4+ 引入),表示记录慢查询的比例(默认为 1.0,即 100%)。

解释输出

slowms: 100 :表示当前慢查询阈值为 100 毫秒。任何执行时间超过 100 毫秒的查询都会被记录到 system.profile 集合中。

was: 1 :表示当前 profiling 级别为 1,即只记录慢查询。

3. 修改慢查询阈值

如果你想修改慢查询阈值,可以使用 db.setProfilingLevel() 方法。例如,将阈值改为 200 毫秒:

javascript 复制代码
db.setProfilingLevel(1, { slowms: 200 })

这代表会记录执行时间超过200毫秒的语句。

然后让 MongoDB 运行一段时间,以便收集足够的慢查询数据。这段时间可以根据你的系统负载和查询频率来决定。

4. 查询 profiling 级别

如果你只想查询当前的 profiling 级别(而不关心阈值),可以使用以下命令:

javascript 复制代码
db.getProfilingLevel()

输出示例:

javascript 复制代码
1
  • 0:关闭 profiling。

  • 1:记录慢查询。

  • 2:记录所有查询。

二、查询慢SQL

1.查询语句

javascript 复制代码
db.system.profile.find().sort({ ts: -1 }).limit(10)

2.过滤 millis 大于 3000 的查询

运行以下命令

javascript 复制代码
db.system.profile.find({ millis: { $gt: 3000 } }).sort({ ts: -1 })
  • { millis: { $gt: 3000 } }:筛选 millis 大于 3000 的文档。

  • .sort({ ts: -1 }):按时间戳倒序排列,确保最新的查询排在最前面。

3. 进一步筛选

如果你只想查看特定类型的操作(如 queryaggregate),可以在查询中添加额外的过滤条件。例如:

  • 只查看 query 操作:

    javascript 复制代码
    db.system.profile.find({ op: "query", millis: { $gt: 3000 } }).sort({ ts: -1 })
  • 只查看 aggregate 操作:

javascript 复制代码
db.system.profile.find({ op: "command", "command.aggregate": { $exists: true }, millis: { $gt: 3000 } }).sort({ ts: -1 })

4. 限制返回结果数量

如果结果较多,可以使用 limit 限制返回的文档数量。例如,只返回前 10 条:

javascript 复制代码
db.system.profile.find({ millis: { $gt: 3000 } }).sort({ ts: -1 }).limit(10)

5.查询指定日期时间的记录

假设你想查询 2023-10-01T12:00:00Z 之后的记录,可以使用以下命令:

javascript 复制代码
db.system.profile.find({
  ts: { $gt: ISODate("2023-10-01T12:00:00Z") }
}).sort({ ts: -1 }).limit(10)

如果你希望查询某个特定日期时间范围内的记录,可以结合 $gt$lt 操作符。

示例:查询 2023-10-01T12:00:00Z2023-10-02T12:00:00Z 之间的记录

javascript 复制代码
db.system.profile.find({
  ts: {
    $gt: ISODate("2023-10-01T12:00:00Z"),
    $lt: ISODate("2023-10-02T12:00:00Z")
  }
}).sort({ ts: -1 }).limit(10);

6.慢查询日志的字段说明

db.system.profile.find() 返回的文档中,以下字段最为关键:

字段名 说明
op 操作类型(如 queryupdateinsert 等)。
ns 操作的命名空间(格式为 数据库名.集合名)。
command 具体的查询或命令内容(如 findaggregate 等)。
millis 查询执行时间(单位:毫秒)。
planSummary 查询的执行计划摘要(如使用的索引)。
ts 查询执行的时间戳。
keysExamined 扫描的索引键数量。
docsExamined 扫描的文档数量。
nreturned 返回的文档数量。
locks 锁信息(如锁类型和持有时间)。

7. 注意事项

  • system.profile 集合的大小:system.profile 是一个 capped collection(固定大小集合),默认大小较小。如果需要更长时间的日志,可以调整其大小:

    javascript 复制代码
    db.setProfilingLevel(0); // 先关闭 profiling
    db.system.profile.drop();
    db.createCollection("system.profile", { capped: true, size: 1048576 }); // 设置新的大小(单位:字节)
    db.setProfilingLevel(1, { slowms: 100 }); // 重新启用 profiling
  • 性能影响:启用 profiling 会对性能有一定影响,建议在生产环境中谨慎使用。

三、创建索引

1.创建

针对以上捞出来的慢SQL语句,可以添加相应的索引

javascript 复制代码
db.mytable.createIndex({ field1: 1, field2: 1 })

值1是对field1字段创建升序索引。

2.验证

创建索引后,可以使用 explain 方法验证查询是否使用了新创建的索引。

javascript 复制代码
db.mytable.find(
  { ‌field1: 'abc', ‌field2: '2025-03-19' }
).limit(1).explain("executionStats")

检查输出中的以下字段:

  • winningPlan.inputStage.indexName:确认是否使用了新创建的索引。

  • executionStats.totalDocsExamined:检查扫描的文档数量是否显著减少。

  • executionStats.executionTimeMillis:检查查询执行时间是否减少。

3.索引优化建议

  • 索引顺序 :复合索引的顺序非常重要。如果查询条件中 field1是等值查询,而 field2是范围查询,那么将 field11放在前面会更高效。

  • 索引选择性:选择性高的字段(即唯一值多的字段)放在索引前面。例如,如果 field1的唯一值比 field2多,则将 field1放在前面。

  • 覆盖索引:如果查询只需要返回索引字段,可以创建一个覆盖索引,避免回表查询。例如:

    javascript 复制代码
    db.mytable.createIndex({ ‌field1: 1, ‌field2: 1, other_field: 1 })

4.其他优化建议

  • 数据分布 :检查 field1field2的数据分布。如果某个字段的值非常集中(如 field2总是相同),索引的效果可能不明显。

  • 查询模式 :如果查询模式固定(如总是查询 field1field2),复合索引是最佳选择。如果查询模式多变,可能需要创建多个索引。

  • 索引大小:索引会占用存储空间,并影响写入性能。确保索引的收益大于其成本。

相关推荐
77美式16 小时前
Node + Express + MongoDB 后端部署全解析:新手零踩坑
数据库·mongodb·express
知识分享小能手1 天前
MongoDB入门学习教程,从入门到精通,MongoDB 持久性完全指南(20)
数据库·学习·mongodb
爬山算法2 天前
MongoDB(86)如何使用MongoDB存储大文件?
数据库·mongodb
知识分享小能手2 天前
MongoDB入门学习教程,从入门到精通,MongoDB的了解应用程序的动态(18)
数据库·学习·mongodb
知识分享小能手2 天前
MongoDB入门学习教程,从入门到精通,MongoDB 安全完全指南(19)
学习·安全·mongodb
disgare3 天前
MongoDB 底层原理
数据库·mongodb
gihigo19984 天前
基于MSComm控件的PC串口通信程序(中断方式接收数据)
数据库·mongodb
知识分享小能手4 天前
MongoDB入门学习教程,从入门到精通,MongoDB的分片管理(17)
数据库·学习·mongodb
清风6666664 天前
基于单片机的自动存包柜设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计·期末大作业
爬山算法4 天前
MongoDB(88)如何进行数据迁移?
数据库·mongodb