【后端高阶面经:MongoDB篇】40、怎么优化MongoDB的查询性能?

在这里插入图片描述

一、索引优化:构建高效查询的基石

(一)索引类型与适用场景

1. 五大核心索引类型
索引类型适用场景示例代码性能影响
单字段索引单条件查询(如用户ID、状态字段)db.users.createIndex({ user_id: 1 })
复合索引多条件组合查询/排序(如状态+时间)db.orders.createIndex({ status: 1, time: -1 })
多键索引数组字段查询(如标签、商品规格)db.products.createIndex({ specs.size: 1 })
文本索引全文搜索(如文章内容、评论)db.articles.createIndex({ content: "text" })
哈希索引分片键/等值查询(需均匀分布数据)sh.shardCollection("data", { _id: "hashed" })
2. 复合索引设计黄金法则(ESR原则)
  • E(Equality等值查询):优先放置等值查询字段(如user_id
  • S(Sort排序):其次放置排序字段(如create_time
  • R(Range范围查询):最后放置范围查询字段(如price
    示例
// 查询:status=paid + 按create_time降序 + price>100
db.orders.createIndex({ status: 1, create_time: -1, price: 1 })

(二)覆盖索引与避免回表

1. 覆盖索引原理
  • 定义:索引包含查询所需的所有字段,无需访问文档数据
  • 优势:减少磁盘I/O,提升查询速度
    示例
// 创建覆盖索引(包含status、create_time、amount)
db.orders.createIndex({ status: 1, create_time: -1 }, { amount: 1 })// 使用覆盖索引查询
db.orders.find({ status: "paid" }, { create_time: 1, amount: 1 } // 字段均在索引中
).hint("status_1_create_time_-1") // 强制使用索引
2. 回表优化对比
操作覆盖索引(命中)非覆盖索引(回表)
扫描类型索引扫描(IXSCAN)索引扫描+文档扫描(COLLSCAN)
内存占用
示例延迟20ms120ms

二、查询模式优化:减少数据扫描量

(一)规避全集合扫描

1. 低效操作符优化
反例(全扫描)正例(索引友好)性能提升
db.users.find({ email: /@gmail$/ })db.users.find({ email: { $regex: "^user" } })10倍+
db.orders.find({ qty: { $exists: true } })db.orders.createIndex({ qty: 1 }); db.orders.find({ qty: { $gt: 0 } })5倍+
2. 前缀匹配优化
// 反例:后缀匹配(无法使用索引)
db.users.find({ email: /@gmail.com$/ })// 正例:前缀匹配(可使用索引)
db.users.find({ email: /^admin/ })

(二)分页查询性能优化

1. 传统分页(skip+limit)的缺陷
  • 问题skip(n)会扫描前n条文档,深度分页时性能骤降
  • 示例db.orders.find().skip(100000).limit(10)需扫描100010条文档
2. 游标分页(基于排序字段)
// 按时间戳排序,记录最后一条的时间戳
const lastTime = new Date("2023-10-01T00:00:00");// 下一页查询
db.orders.find({ create_time: { $lt: lastTime } 
}).sort({ create_time: -1 }).limit(10)
3. 键值分页(基于_id)
// 记录最后一条的_id
const lastId = ObjectId("6401f015f9b1b4f2a1c000001");// 下一页查询
db.orders.find({ _id: { $gt: lastId } 
}).sort({ _id: 1 }).limit(10)

(三)聚合管道优化

1. 管道阶段顺序优化
  • 原则:尽早过滤数据($match前置),减少后续阶段处理量
    示例
// 反例:先分组再过滤(处理全量数据)
db.sales.aggregate([{ $group: { _id: "$product", total: { $sum: "$amount" } } },{ $match: { total: { $gt: 1000 } } }
])// 正例:先过滤再分组(仅处理符合条件的数据)
db.sales.aggregate([{ $match: { amount: { $gt: 10 } } }, // 前置过滤{ $group: { _id: "$product", total: { $sum: "$amount" } } }
])
2. 使用索引加速聚合
// 创建复合索引
db.sales.createIndex({ product: 1, amount: 1 })// 聚合时使用索引
db.sales.aggregate([{ $match: { product: "P001" } },{ $group: { _id: null, total: { $sum: "$amount" } } }
]).hint({ product: 1, amount: 1 })

三、分片集群优化:水平扩展查询能力

(一)分片键选择策略

1. 三大分片键类型对比
类型适用场景示例字段数据分布查询性能
哈希分片高并发写、均匀分布user_id、order_id均衡等值查询高效
范围分片时间序列、范围查询create_time、date可能热点范围查询高效
复合分片混合查询需求region+time较均衡组合查询高效
2. 分片键设计禁忌
  • 避免低基数字段:如status(仅少数取值,导致数据倾斜)
  • 避免频繁更新字段:如last_login(影响分片稳定性)
3. 分片集群部署示例
客户端
mongos路由节点
Shard1副本集
Shard2副本集
主节点
从节点
主节点
从节点

(二)分片集群查询流程

  1. 路由阶段:mongos解析查询,确定目标Shard
  2. 并行查询:各Shard执行本地查询(利用本地索引)
  3. 结果合并:mongos聚合各Shard结果,返回客户端

优化点

  • 确保分片键包含在查询条件中,避免全分片扫描
  • 为每个Shard的本地集合创建复合索引

四、硬件与配置调优:释放底层性能

(一)内存配置最佳实践

1. WiredTiger引擎参数
# mongod.conf配置示例
storage:wiredTiger:engineConfig:cacheSizeGB: 32  # 建议为物理内存的50%-80%,确保索引常驻内存collectionConfig:blockSize: 4096  # 减小块大小,提升小文档查询效率
2. 内存使用监控
# 查看内存使用情况
db.serverStatus().mem
# 关键指标:
# - "resident":常驻内存大小(理想值接近cacheSizeGB)
# - "virtual":虚拟内存使用(应避免过高,否则触发swap)

(二)磁盘与文件系统优化

1. 存储介质选择
类型随机IOPS延迟适用场景成本
NVMe SSD20000+<1ms主节点、热数据
SATA SSD5000+1-5ms从节点、温数据
HDD200+10-20ms冷数据、备份
2. 文件系统配置
# 禁用透明大页(THP)提升性能
echo never > /sys/kernel/mm/transparent_hugepage/enabled# XFS文件系统挂载选项
mount -t xfs -o noatime,nodiratime /dev/nvme0n1p1 /data/mongodb

(三)读写关注调优

1. 写入关注(Write Concern)
场景配置延迟(ms)数据可靠性
日志写入{ w: 1 }1-5弱一致
订单创建{ w: majority }5-20强一致
资产变更{ w: majority, j: true }20-50最强一致
2. 读取关注(Read Preference)
// 从Secondary节点读取(读扩展)
db.orders.find().readPreference("secondaryPreferred")// 从最近的节点读取(全球部署)
db.orders.find().readPreference("nearest")

五、监控与分析:定位性能瓶颈

(一)执行计划分析(Explain)

1. 核心指标解析
const plan = db.orders.find({ status: "paid" }).explain("executionStats")
指标含义优化目标
executionTimeMillis总执行时间<100ms
totalDocsExamined扫描的文档数尽可能接近查询结果数
nReturned返回的文档数等于查询结果数
stage执行阶段(如IXSCAN/COLLSCAN)确保为IXSCAN(索引扫描)
2. 优化示例
// 优化前:COLLSCAN(全表扫描)
db.orders.find({ customer: "Alice" }).explain()// 优化后:IXSCAN(索引扫描)
db.orders.createIndex({ customer: 1 })
db.orders.find({ customer: "Alice" }).explain()

(二)慢查询日志

1. 配置慢查询监控
# mongod.conf
operationProfiling:mode: slowOpslowOpThresholdMs: 100  # 慢查询阈值(毫秒)slowOpSampleRate: 1.0   # 采样率(1.0表示记录所有慢查询)
2. 分析慢查询日志
# 查看慢查询统计
db.system.profile.find({ts: { $gt: new Date("2023-10-01") },millis: { $gt: 100 }
}).sort({ millis: -1 })

六、实战案例:电商订单系统性能优化

(一)场景描述

  • 数据规模:订单量10亿条,日均新增100万条
  • 高频查询
    1. 按用户ID查询最近100条订单(user_id + create_time
    2. 统计已支付订单总量(status=paid
    3. 按日期范围查询订单金额分布(create_time + amount

(二)优化前性能指标

查询类型平均延迟扫描文档数索引使用情况
用户订单查询800ms10000+未命中索引
支付统计1200ms全表扫描无索引
范围查询1500ms500万+部分索引命中

(三)优化方案实施

1. 索引优化
// 用户订单查询索引(覆盖查询)
db.orders.createIndex({ user_id: 1, create_time: -1 
}, { amount: 1, status: 1 
})// 支付统计索引
db.orders.createIndex({ status: 1 })// 范围查询索引
db.orders.createIndex({ create_time: 1, amount: 1 })
2. 分片策略
// 哈希分片(用户ID均匀分布)
sh.shardCollection("ecommerce.orders", { user_id: "hashed" })
3. 查询改写
// 优化后用户订单查询(游标分页)
const lastTime = new Date("2023-10-05T00:00:00");
db.orders.find({user_id: "U123",create_time: { $lt: lastTime }
}).sort({ create_time: -1 })
.limit(100)
.hint("user_id_1_create_time_-1")

(四)优化后性能指标

查询类型平均延迟扫描文档数索引使用情况
用户订单查询65ms100条覆盖索引命中
支付统计45ms1200条单字段索引命中
范围查询180ms5000条复合索引命中

七、面试核心考点与应答策略

(一)基础问题

  1. Q:如何判断查询是否使用了索引?
    A:使用explain()分析执行计划,若stageIXSCAN则命中索引;查看totalDocsExamined是否接近查询结果数,若远大于则可能全表扫描。

  2. Q:复合索引的字段顺序如何影响性能?
    A:遵循ESR原则:等值查询字段→排序字段→范围查询字段。例如,查询status=paid AND time>2023-01-01 AND sort by amount,索引应为{status:1, time:1, amount:1}

(二)进阶问题

  1. Q:深度分页为什么慢?如何优化?
    A

    • 原因:skip(n)需扫描前n条文档,时间复杂度O(n)
    • 优化:
      1. 使用search_after基于排序字段分页
      2. 记录最后一条的排序值,通过范围查询替代skip
      db.orders.find({ create_time: { $lt: last_time } }).sort({ create_time: -1 }).limit(10)
      
  2. Q:分片集群中如何避免数据倾斜?
    A

    • 选择高基数分片键(如用户ID哈希)
    • 监控块分布,通过sh.rebalanceShard()手动迁移热点块
    • 启用自动平衡器(默认开启),调整块大小(如256MB)

(三)架构设计问题

Q:设计一个千万级数据的查询系统,如何优化MongoDB性能?
回答思路

  1. 索引层
    • 为高频查询字段创建复合索引,确保覆盖查询
    • 使用文本索引优化全文搜索场景
  2. 集群层
    • 分片集群部署,哈希分片键均匀分布数据
    • 独立部署mongos节点,横向扩展路由能力
  3. 存储层
    • 使用SSD存储热数据,HDD存储冷数据
    • 调整WiredTiger缓存大小,确保索引常驻内存
  4. 查询层
    • 避免skip深度分页,改用游标分页
    • 聚合查询前置过滤条件,减少数据处理量

八、性能优化的黄金法则

(一)索引优先原则

  • 80%的性能问题可通过优化索引解决,优先分析查询是否命中索引
  • 定期清理冗余索引(db.xxx.getIndexes()),减少写入开销

(二)数据分片原则

  • 单集合数据量超过1TB时启用分片,分片键选择需平衡查询与分布
  • 每个Shard节点数≥3(1主2从),确保高可用

(三)监控驱动原则

  • 建立常态化监控:索引使用率、慢查询频率、分片负载均衡
  • 使用mongostat实时监控节点状态,mongotop分析读写分布

(四)渐进优化原则

  1. 分析:通过explain()和慢查询日志定位瓶颈
  2. 验证:小范围测试优化方案(如灰度环境)
  3. 部署:滚动更新索引或分片配置,避免服务中断
  4. 监控:对比优化前后性能指标,持续迭代

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/82580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux wget 常用命令详解

目录 1.1 工具定位 基础下载示例 二、高效下载参数详解 2.1 下载控制类 2.2 文件管理类 2.3 网络优化类 三、高级应用场景 3.1 递归下载与整站镜像 3.2 自动化下载实践 3.3 安全下载配置 四、参数速查手册 4.1 常用参数汇总 1.1 工具定位 基础下载语法 wget [选项…

Pytorch中文文本分类

本文为&#x1f517;365天深度学习训练营内部文章 原作者&#xff1a;K同学啊 将对中文文本进行分类&#xff0c;示例如下&#xff1a; 文本分类流程图 1.加载数据 import time import pandas as pd import torch from torch.utils.data import DataLoader, random_split impo…

13.「极简」扣子(coze)教程 | 小程序UI设计进阶(三)让界面动起来,实操讲透“聚焦”事件

前一期大师兄介绍了扣子平台组件的两种状态“禁用”和“加载”。这两种方法使控件可以通过简单设置表示出更多的运行状态。今天大师兄将详细介绍控件的一种事件“聚焦”。 扣子&#xff08;coze&#xff09;编程 「极简」扣子(coze)教程 | 小程序UI设计进阶 II&#xff01;让…

剑指offer11_矩阵中的路径

矩阵中的路径 请设计一个函数&#xff0c;用来判断在一个矩阵中是否存在一条路径包含的字符按访问顺序连在一起恰好为给定字符串。 路径可以从矩阵中的任意一个格子开始&#xff0c;每一步可以在矩阵中向左&#xff0c;向右&#xff0c;向上&#xff0c;向下移动一个格子。 如…

腾讯2025年校招笔试真题手撕(三)

一、题目 今天正在进行赛车车队选拔&#xff0c;每一辆赛车都有一个不可以改变的速度。现在需要选取速度差距在10以内的车队&#xff08;车队中速度的最大值减去最小值不大于10&#xff09;&#xff0c;用于迎宾。车队的选拔按照的是人越多越好的原则&#xff0c;给出n辆车的速…

《三维点如何映射到图像像素?——相机投影模型详解》

引言 以三维投影介绍大多比较分散&#xff0c;不少小伙伴再面对诸多的坐标系转换中容易弄混&#xff0c;特别是再写代码的时候可能搞错&#xff0c;所有这篇文章帮大家完整的梳理3D视觉中的投影变换的全流程&#xff0c;一文弄清楚这个过程&#xff0c;帮助大家搞清坐标系转换…

Ini配置文件读写,增加备注功能

1.增加备注项写入 例: #节点备注 [A] #项备注 bbb1 ccc2 [B] bbb1 IniConfig2 ic new IniConfig2(); //首次写入 if (!ic.CanRead()) { ic.AddSectionReMarke("A", "节点备注"); ic.SetValue("A&qu…

OpenHarmony 5.0中状态栏添加以太网状态栏图标以及功能实现

目录 1.前置条件 2.方案 1.前置条件 首先以太网接口是有问题的,如下按照如下流程将以太网接口进行修复 OpenHarmony 以太网卡热插拔事件接口无效-CSDN博客 然后上述的接口可以了就可以通过这个接口获取以太网是否连接状态 要注意wifi连接的干扰和预置虚拟网口干扰 2.方案…

RNN GRU LSTM 模型理解

一、RNN 1. 在RNN中&#xff0c; 2. RNN是一个序列模型&#xff0c;与非序列模型不同&#xff0c;序列中的元素互相影响&#xff1a; 是由 计算得来的。 在前向传播中&#xff1a; 用于计算 和 用于计算 和 因此&#xff0c;当进行反向链式法则求导时候&#xf…

多路径传输(比如 MPTCP)控制实时突发

实时突发很难控制&#xff0c;因为 “实时” 和 “突发” 相互斥。实时要求避免排队&#xff0c;而突发必然要排队&#xff0c;最终的解决方案都指向找一个公说公有理&#xff0c;婆说婆有理的中间点&#xff0c;这并没解决问题&#xff0c;只是权衡了问题。 这种局部解决问题的…

函数式编程思想详解

函数式编程思想详解 1. 核心概念 不可变数据 (Immutable Data) 数据一旦创建&#xff0c;不可修改。任何操作均生成新数据&#xff0c;而非修改原数据。 优点&#xff1a;避免副作用&#xff0c;提升并发安全&#xff0c;简化调试。 Java实现&#xff1a;使用final字段、不可变…

iOS 主要版本发布历史

截至 2025 年 5 月&#xff0c;iOS 的最新正式版本是 iOS 18&#xff0c;于 2024 年 9 月 16 日 正式发布。此前的 iOS 17 于 2023 年 9 月 18 日 发布&#xff0c;并在 2024 年被 iOS 18 取代。(维基百科) &#x1f4f1; iOS 主要版本发布历史 以下是 iOS 各主要版本的发布日…

矩阵详解:线性代数在AI大模型中的核心支柱

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#, Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开…

基于51单片机和8X8点阵屏、独立按键的飞行躲闪类小游戏

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、独立按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 用的是普中A2开发板。 【单片机】STC89C52RC 【频率】12T11.0592MHz 【外设】8X8点阵屏、独立按键 效果查看/操作演示&#xff…

区块链可投会议CCF C--APSEC 2025 截止7.13 附录用率

Conference&#xff1a;32nd Asia-Pacific Software Engineering Conference (APSEC 2025) CCF level&#xff1a;CCF C Categories&#xff1a;软件工程/系统软件/程序设计语言 Year&#xff1a;2025 Conference time&#xff1a;December 2-5, 2025 in Macao SAR, China …

pdf图片导出(Visio\Origin\PPT)

一、Visio 导入pdf格式图片 1. 设计->大小&#xff0c;适应绘图。 2. 文件->导出&#xff0c;导出为pdf格式。 上面两部即可得到只包含图的部分的pdf格式。 如果出现的有默认白边&#xff0c;可以通过以下方式设置&#xff1a; 1. 文件->选项->自定义功能区->…

vector的实现

介绍 1. 本质与存储结构 动态数组实现&#xff1a;vector 本质是动态分配的数组&#xff0c;采用连续内存空间存储元素&#xff0c;支持下标访问&#xff08;如 vec[i]&#xff09;&#xff0c;访问效率与普通数组一致&#xff08;时间复杂度 O (1)&#xff09;。动态扩容机制&…

【Linux笔记】防火墙firewall与相关实验(iptables、firewall-cmd、firewalld)

一、概念 1、防火墙firewall Linux 防火墙用于控制进出系统的网络流量&#xff0c;保护系统免受未授权访问。常见的防火墙工具包括 iptables、nftables、UFW 和 firewalld。 防火墙类型 包过滤防火墙&#xff1a;基于网络层&#xff08;IP、端口、协议&#xff09;过滤流量&a…

el-date-picker 前端时间范围选择器

控制台参数&#xff1a; 前端代码&#xff1a;用数组去接受&#xff0c;同时用 value-format"YYYY-MM-DD" 格式化值为&#xff1a;年月日格式 <!-- 查询区域 --><transition name"fade"><div class"search" v-show"showSe…

在 macOS 上安装 jenv 管理 JDK 版本

在 macOS 上安装 jenv 并管理 JDK 版本 在开发 Java 应用程序时&#xff0c;你可能需要在不同的项目中使用不同版本的 JDK。手动切换 JDK 版本可能会很繁琐&#xff0c;但幸运的是&#xff0c;有一个工具可以简化这个过程&#xff1a;jenv。jenv 是一个流行的 Java 版本管理工…