MongoDB 的慢日志(Slow Query Log)对于运维和步伐员来说都非常紧张,因为它直接关系到数据库的性能和应用步伐的稳定性。以下分享介绍下MongoDB慢日志查询及索引创建相关的一些笔记。
一,准备
1. 利用 db.currentOp() 实时监控
db.currentOp() 可以查看当前正在实行的操纵,得当捕捉瞬时的高 CPU 操纵。
示例:过滤长时间运行的操纵
- db.currentOp({ "secs_running": { "$gt": 1 } }) // 过滤运行时间超过 1 秒的操作
复制代码 关键字段:
- opid:操纵 ID。
- op:操纵类型(如 query、update、command 等)。
- ns:操纵的定名空间(数据库和聚集)。
- secs_running:操纵已经运行的时间(秒)。
- planSummary:操纵的实行操持择要。
- locks:操纵持有的锁信息。
2. 查询当前 profiling 状态
运行以下命令:
输出示例:
- {
- "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 毫秒:
- db.setProfilingLevel(1, { slowms: 200 })
复制代码 这代表会记录实行时间高出200毫秒的语句。
然后让 MongoDB 运行一段时间,以便收集足够的慢查询数据。这段时间可以根据你的体系负载和查询频率来决定。
4. 查询 profiling 级别
如果你只想查询当前的 profiling 级别(而不关心阈值),可以利用以下命令:
输出示例:
- 0:关闭 profiling。
- 1:记录慢查询。
- 2:记录全部查询。
二、查询慢SQL
1.查询语句
- db.system.profile.find().sort({ ts: -1 }).limit(10)
复制代码 2.过滤 millis 大于 3000 的查询
运行以下命令
- db.system.profile.find({ millis: { $gt: 3000 } }).sort({ ts: -1 })
复制代码
- { millis: { $gt: 3000 } }:筛选 millis 大于 3000 的文档。
- .sort({ ts: -1 }):按时间戳倒序排列,确保最新的查询排在最前面。
3. 进一步筛选
如果你只想查看特定类型的操纵(如 query 或 aggregate),可以在查询中添加额外的过滤条件。例如:
- 只查看 query 操纵:
- db.system.profile.find({ op: "query", millis: { $gt: 3000 } }).sort({ ts: -1 })
复制代码 - 只查看 aggregate 操纵:
- db.system.profile.find({ op: "command", "command.aggregate": { $exists: true }, millis: { $gt: 3000 } }).sort({ ts: -1 })
复制代码 4. 限定返回结果数量
如果结果较多,可以利用 limit 限定返回的文档数量。例如,只返回前 10 条:
- db.system.profile.find({ millis: { $gt: 3000 } }).sort({ ts: -1 }).limit(10)
复制代码 5.查询指定日期时间的记录
假设你想查询 2023-10-01T12:00:00Z 之后的记录,可以利用以下命令:
- db.system.profile.find({
- ts: { $gt: ISODate("2023-10-01T12:00:00Z") }
- }).sort({ ts: -1 }).limit(10)
复制代码 如果你希望查询某个特定日期时间范围内的记录,可以结合 $gt 和 $lt 操纵符。
示例:查询 2023-10-01T12:00:00Z 到 2023-10-02T12:00:00Z 之间的记录
- 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操纵类型(如 query、update、insert 等)。ns操纵的定名空间(格式为 数据库名.聚集名)。command具体的查询或命令内容(如 find、aggregate 等)。millis查询实行时间(单元:毫秒)。planSummary查询的实行操持择要(如利用的索引)。ts查询实行的时间戳。keysExamined扫描的索引键数量。docsExamined扫描的文档数量。nreturned返回的文档数量。locks锁信息(如锁类型和持偶然间)。 7. 留意事项
- system.profile 聚集的大小:system.profile 是一个 capped collection(固定大小聚集),默认大小较小。如果必要更长时间的日志,可以调整其大小:
- 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语句,可以添加相应的索引
- db.mytable.createIndex({ field1: 1, field2: 1 })
复制代码 值1是对field1字段创建升序索引。
2.验证
创建索引后,可以利用 explain 方法验证查询是否利用了新创建的索引。
- 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放在前面。
- 覆盖索引:如果查询只必要返回索引字段,可以创建一个覆盖索引,制止回表查询。例如:
- db.mytable.createIndex({ field1: 1, field2: 1, other_field: 1 })
复制代码 4.其他优化发起
- 数据分布:检查 field1和 field2的数据分布。如果某个字段的值非常集中(如 field2总是雷同),索引的效果大概不明显。
- 查询模式:如果查询模式固定(如总是查询 field1和 field2),复合索引是最佳选择。如果查询模式多变,大概必要创建多个索引。
- 索引大小:索引会占用存储空间,并影响写入性能。确保索引的收益大于其成本。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |