当前位置: 首页 > news >正文

mongodb 慢查询模拟

MongoDB 作为非关系型数据库,虽无 “表”“SQL” 概念,但慢查询的核心诱因(无索引、全集合扫描、复杂聚合)与 MySQL 类似。以下参考 MySQL 慢查询模拟逻辑,从 “环境准备 - 数据构造 - 慢查询触发 - 排查 - 优化” 全流程,以 “用户集合(user)” 为核心场景展开,确保操作可复现。​
一、前期准备:MongoDB 环境与工具搭建(类比 MySQL 环境)​

  1. 环境部署​
    本地部署:下载 MongoDB 社区版(5.0 + 版本),解压后执行mongod --dbpath 数据存储路径启动服务,默认端口 27017;​
    Docker 部署(更便捷):​


    连接验证:使用 MongoDB 自带客户端mongo(或可视化工具 Robo 3T、Compass)连接,执行show dbs确认服务正常。​
  2. 工具准备​
    客户端工具:Robo 3T(可视化操作集合、执行查询)、MongoDB Compass(自带性能分析功能);​
    慢查询分析工具:MongoDB 内置的db.currentOp()(查看当前运行操作)、system.profile(慢查询日志集合)。​
    二、第一步:构造用户集合与大量模拟数据(类比 MySQL 建表 + 插数据)​
  3. 创建数据库与集合(类比 MySQL 建库建表)​
    MongoDB 无需提前创建数据库(插入数据时自动创建),直接创建 “用户集合(user)”,字段设计参考 MySQL 用户表,贴合业务查询场景:​

  4. 生成 100 万条模拟数据(类比 MySQL 存储过程插数据)​
    通过 MongoDB 的bulkWrite批量插入(效率高于单条插入),生成符合业务分布的数据(如 city 覆盖 30 个城市、age 18-60 岁、createTime 近 2 年):​


    三、第二步:执行查询触发慢查询(类比 MySQL 低效 SQL)​
    MongoDB 慢查询的核心诱因是 “全集合扫描(类似 MySQL 全表扫描)”“无索引复杂聚合”,设计 4 类典型低效查询,触发慢查询(默认慢查询阈值为 100ms,超过即视为慢查询)。​
  5. 场景 1:无索引单条件过滤(类比 MySQL 无索引单表查询)​
    查询 “北京市用户数量”,city字段无索引,触发全集合扫描:​

  6. 场景 2:无索引多条件组合查询(类比 MySQL 多条件无索引)​
    查询 “2024 年注册、年龄 25-30 岁的女性用户列表”,createTime“age”“gender” 均无索引:​

  7. 场景 3:索引失效(类比 MySQL 函数导致索引失效)​
    先为createTime创建索引,再用$expr函数操作字段,导致索引失效:​

    // 1. 为createTime创建单字段索引​
    db.user.createIndex({ createTime: 1 });​
    print("已创建createTime索引");​

    // 2. 用$expr函数操作createTime,导致索引失效​
    const start3 = Date.now();​
    const june2024Users = db.user.countDocuments({​
    $expr: { $eq: [{ $dateToString: { format: "%Y-%m", date: "$createTime" } }, "2024-06"] }​
    });​
    const cost3 = Date.now() - start3;​
    print(函数操作索引字段查询2024年6月用户数:${june2024Users},耗时:${cost3}ms);​
    // 预期结果:耗时约600-2500ms(索引失效,全集合扫描)​
  8. 场景 4:无索引聚合排序(类比 MySQL 无索引 GROUP BY+ORDER BY)​
    查询 “各城市用户数量 TOP10”,city无索引,聚合操作需全集合扫描后排序:​

    const start4 = Date.now();​

    // 无索引聚合排序​
    const cityUserTop10 = db.user.aggregate([​
    { $group: { _id: "$city", userCount: { $sum: 1 } } }, // 按city分组统计​
    { $sort: { userCount: -1 } }, // 按用户数降序​
    { $limit: 10 } // 取TOP10​
    ]).toArray();​

    const cost4 = Date.now() - start4;​
    print(无索引聚合城市用户TOP10耗时:${cost4}ms);​
    // 预期结果:耗时约1000-3500ms(全集合扫描+分组+排序)​

    四、第三步:慢查询排查(类比 MySQL 慢查询日志 + EXPLAIN)​
    MongoDB 通过 “慢查询日志(system.profile)” 和 “执行计划(explain)” 排查慢查询,核心逻辑与 MySQL 类似。​
  9. 启用慢查询日志(类比 MySQL slow_query_log)​
    MongoDB 通过db.setProfilingLevel()启用慢查询记录,记录存储在system.profile集合中:​

    // 1. 启用慢查询日志:level=1表示记录超过threshold(毫秒)的查询,这里设为100ms​
    db.setProfilingLevel(1, { slowms: 100 });​
    print("已启用慢查询日志,记录耗时>100ms的操作");​

    // 2. 查看慢查询记录(筛选user集合的慢查询)​
    const slowQueries = db.system.profile.find({​
    ns: "slow_query_test.user", // 命名空间(数据库.集合)​
    op: { $in: ["query", "count", "aggregate"] } // 操作类型(查询、计数、聚合)​
    }).sort({ ts: -1 }).limit(10).toArray();​

    // 打印慢查询详情(重点看durationMillis、planSummary)​
    slowQueries.forEach((query, index) => {​
    print(\n慢查询${index+1}:);​
    print(操作类型:${query.op});​
    print(耗时:${query.durationMillis}ms);​
    print(查询条件:${JSON.stringify(query.query)});​
    print(执行计划概要:${query.planSummary}); // 全集合扫描会显示COLLSCAN​
    });​
  10. 查看执行计划(类比 MySQL EXPLAIN)​
    通过explain()分析查询的执行计划,判断是否全集合扫描、是否使用索引:​

    // 分析场景1(无索引查北京用户)的执行计划​
    const explain1 = db.user.explain("executionStats").countDocuments({ city: "北京" });​
    print("\n场景1执行计划:");​
    print(扫描类型:${explain1.executionStats.executionStages.stage}); // COLLSCAN=全集合扫描​
    print(扫描文档数:${explain1.executionStats.totalDocsExamined}); // 1000000(全量扫描)​
    print(返回结果数:${explain1.executionStats.nReturned});​

    // 分析场景3(索引失效)的执行计划​
    const explain3 = db.user.explain("executionStats").countDocuments({​
    $expr: { $eq: [{ $dateToString: { format: "%Y-%m", date: "$createTime" } }, "2024-06"] }​
    });​
    print("\n场景3执行计划:");​
    print(扫描类型:${explain3.executionStats.executionStages.stage}); // 仍为COLLSCAN(索引失效)​
    print(是否使用索引:${explain3.queryPlanner.winningPlan.inputStage.stage === "IXSCAN"}); // false​

    五、第四步:慢查询优化(类比 MySQL 索引 + SQL 优化)​
    MongoDB 优化核心仍是 “索引优化”+“查询逻辑优化”,针对上述场景逐一优化:​
  11. 场景 1 优化:为 city 创建单字段索引​

    // 创建city索引​
    db.user.createIndex({ city: 1 });​
    print("已创建city索引");​

    // 重新执行查询,验证优化效果​
    const start1Opt = Date.now();​
    const beijingUserCountOpt = db.user.countDocuments({ city: "北京" });​
    const cost1Opt = Date.now() - start1Opt;​
    print(索引优化后查询北京用户数:${beijingUserCountOpt},耗时:${cost1Opt}ms);​
    // 预期结果:耗时降至10-50ms(索引扫描IXSCAN)​
  12. 场景 2 优化:为多条件创建复合索引(类比 MySQL 联合索引)​
    按 “过滤频率高→范围字段” 顺序创建复合索引(gender>age>createTime):​

    db.user.createIndex({ gender: 1, age: 1, createTime: 1 });​
    print("已创建gender+age+createTime复合索引");​

    // 重新执行多条件查询​
    const start2Opt = Date.now();​
    const userListOpt = db.user.find({​
    gender: 2,​
    age: { $gte: 25, $lte: 30 },​
    createTime: { $gte: new Date("2024-01-01"), $lte: new Date("2024-12-31") }​
    }).project({ userName: 1, phone: 1, _id: 0 }).toArray();​
    const cost2Opt = Date.now() - start2Opt;​
    print(复合索引优化后多条件查询结果数:${userListOpt.length},耗时:${cost2Opt}ms);​
    // 预期结果:耗时降至15-80ms​
  13. 场景 3 优化:避免函数操作,使用索引范围查询​
    修改查询逻辑,用$gte/$lt替代$expr函数,复用createTime索引:​

    // 优化后查询(无函数操作,使用索引)​
    const start3Opt = Date.now();​
    const june2024UsersOpt = db.user.countDocuments({​
    createTime: { ​
    $gte: new Date("2024-06-01"), ​
    $lt: new Date("2024-07-01") // 左闭右开,避免漏数据​
    }​
    });​
    const cost3Opt = Date.now() - start3Opt;​
    print(优化后查询2024年6月用户数:${june2024UsersOpt},耗时:${cost3Opt}ms);​
    // 预期结果:耗时降至5-30ms(索引扫描IXSCAN)​
  14. 场景 4 优化:为聚合分组字段创建索引​
    复用场景 1 的city索引,聚合操作可利用索引避免全集合扫描:​

    // 重新执行聚合查询(复用city索引)​
    const start4Opt = Date.now();​
    const cityUserTop10Opt = db.user.aggregate([​
    { $group: { _id: "$city", userCount: { $sum: 1 } } },​
    { $sort: { userCount: -1 } },​
    { $limit: 10 }​
    ]).toArray();​
    const cost4Opt = Date.now() - start4Opt;​
    print(索引优化后聚合城市用户TOP10耗时:${cost4Opt}ms);​
    // 预期结果:耗时降至20-100ms​

    六、总结:MongoDB 与 MySQL 慢查询逻辑对比​

    环节​
    MySQL​
    MongoDB​
    核心共性​
    数据存储​
    表(Table)+ 行(Row)​
    集合(Collection)+ 文档(Document)​
    均需构造大量数据触发慢查询​
    慢查询诱因​
    全表扫描、无索引关联、函数失效​
    全集合扫描(COLLSCAN)、无索引聚合​
    核心是 “无索引导致扫描量过大”​
    排查工具​
    slow_query_log、EXPLAIN​
    system.profile、explain()​
    均通过日志 + 执行计划定位问题​
    优化核心​
    单表 / 联合索引、SQL 逻辑优化​
    单字段 / 复合索引、查询逻辑优化​
    均以 “索引优化” 为核心​

    通过类比 MySQL 逻辑,可快速掌握 MongoDB 慢查询的模拟与优化思路,核心是理解 “全集合扫描” 的危害,通过合理设计索引减少数据扫描量,提升查询效率。
http://www.wxhsa.cn/company.asp?id=712

相关文章:

  • Java第一次实验
  • HCIP回顾— BGP经典实验详解
  • 逆波兰表达式求值+滑动窗口最大值
  • 84. 柱状图中最大的矩形
  • 前k个高频元素
  • 千靶日记-0002
  • [序列化/JSON/Java/Utils] JACKSON 概述
  • 完全弹性碰撞公式推导
  • reLeetCode 热题 100-2 字母异位词分组 扩展 - MKT
  • 第6篇、Kafka 高级实战:生产者路由与消费者管理
  • 3.4 页面替换算法 Page Replacement Algorithms
  • 学习心得
  • 反射对JVM的影响
  • reLeetCode 热题 100-2 字母异位词分组 - MKT
  • 分布式id
  • ipad装windows系统模拟器
  • [Java/SQL/Utils] SQL注释清除工具:SqlCommentStripper
  • 大模型面试题
  • CF2021D 题解 | dp
  • Caffeine缓存
  • Spark面试题清单
  • RocketMQ知识点梳理
  • Tekla坐标定位插件源码
  • 记录 使用PsExec启动System权限的WPF 程序
  • std::map的基本用法
  • 力扣20题 有效的括号
  • 2025年9月10日学习笔记之keil软件仿真调试
  • MySQL的explain使用
  • 力扣19题 删除链表的倒数第N个结点
  • 基于LZO的无损数据压缩IP,高性能压缩速率32Gbps,适用于FPGAASIC