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),复合索引是最佳选择。如果查询模式多变,可能需要创建多个索引。

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

相关推荐
The Sheep 202319 分钟前
MongoDB与.Net6
数据库·mongodb
点灯小铭2 小时前
基于单片机的智能收银机模拟系统设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计·期末大作业
数据知道4 小时前
一文掌握 MongoDB 存储引擎 WiredTiger 的原理
数据库·mongodb·数据库架构
清风6666661 天前
基于单片机的电加热炉智能温度与液位PID控制系统设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计·期末大作业
列御寇1 天前
MongoDB分片集概述
数据库·mongodb
列御寇1 天前
MongoDB分片集群——集群组件概述
数据库·mongodb
列御寇1 天前
MongoDB分片集群——mongos组件(mongos进程)
数据库·mongodb
列御寇1 天前
MongoDB分片集群分片模式——哈希分片(Hashed Sharding)
数据库·mongodb·哈希算法
列御寇1 天前
MongoDB分片集群——分片键(Shard Keys)概述
数据库·mongodb
橘橙黄又青3 天前
mongodb的基本命令
数据库·mongodb