当前位置:首页 > 云谷精选

MongoDB出现大量慢查询怎么建索引

admin2026年03月18日云谷精选12.5万
摘要:# MongoDB慢查询卡成狗?别慌,建索引就对了,但别瞎建! 我前两天帮一个做电商的朋友看后台,好家伙,一个商品列表页加载要十几秒。他愁眉苦脸地说:“加了高防,上了CDN,源站带宽也够,怎么还是这么慢?” 我一查数据库日志,满屏的慢查询,全在扫全表(C…

MongoDB慢查询卡成狗?别慌,建索引就对了,但别瞎建!

我前两天帮一个做电商的朋友看后台,好家伙,一个商品列表页加载要十几秒。他愁眉苦脸地说:“加了高防,上了CDN,源站带宽也够,怎么还是这么慢?” 我一查数据库日志,满屏的慢查询,全在扫全表(COLLSCAN)。问题根本不在防护上,而在数据库里——他那MongoDB的集合,压根没建对索引。

这种感觉你懂吧?钱花在刀背上,最该优化的地方却裸奔着。

很多团队一提到数据库优化,就想到加内存、升配置。其实吧,绝大多数MongoDB性能问题,根源就俩字:索引。 或者更准确地说,是缺索引,或者建了没用的索引。今天咱就抛开那些晦涩的官方文档,用大白话聊聊,当MongoDB慢查询多到让你头皮发麻时,到底该怎么建索引。

第一步:先别急着动手,把“元凶”揪出来

很多人的第一反应是:“慢?那我赶紧建几个索引试试。”——打住!这跟生病了乱吃药一个道理。

你得先知道是哪个查询在慢。MongoDB自带的数据库分析器(Profiler) 就是你的“诊断仪”。把它打开(级别设为1,记录慢查询就行),跑一会儿业务。

然后,重点看那些planSummary: "COLLSCAN"的记录。这意思就是:“老弟,你这查询没走索引,我把整个表从头到尾翻了一遍才找到你要的数据。” 对于动辄百万、千万级的集合,这能不慢吗?

关键一步来了: 别只看一条,把同一种查询模式的慢日志多收集几条。你要找的是最频繁出现、且最耗时的查询模式。比如,是不是所有按userIdcreateTime范围查询的订单都慢?这才是你要下刀子的地方。

第二步:建索引的核心心法: ESR 原则

索引不是越多越好。每多一个索引,写操作(增、删、改)就会慢一点,因为数据库要额外维护索引树。所以,怎么用最少的索引,解决最多的问题?

记住这个ESR原则(Equality, Sort, Range),这是MongoDB官方推荐的索引构建最佳实践,说白了就是按优先级排字段

  1. E (等值查询字段) 放最前面:比如 status: "active", category: "book"。这些字段能快速把数据范围缩小到一个很小的子集。
  2. S (排序字段) 放中间:比如 ORDER BY createTime DESC。如果排序字段能被索引覆盖,数据库就不用做费内存的临时排序了。
  3. R (范围查询字段) 放最后:比如 createTime: {$gt: ISODate("...")}age: {$lt: 30}。范围查询本身就会扫描索引的一部分,放最后效率最高。

举个例子: 你最常见的慢查询是:“查找某个用户(userId=xxx)在过去一个月(createTime > 某时间)的已完成(status="completed")订单,并按创建时间倒序(sort by createTime DESC)返回。”

根据ESR原则,最优的复合索引应该是:{ userId: 1, status: 1, createTime: -1 }

  • userIdstatus是等值查询(E)。
  • createTime既用于范围查询(R),又用于排序(S)。因为我们按降序排,所以索引里也设为降序(-1),这样数据库就可以顺着索引读,效率最高。

很多所谓的高端优化方案,PPT很猛,真到建索引时连这个基础原则都忘了,你说能不出问题吗?

第三步:避开这些坑,你的索引才算没白建

  1. 别在低基数字段上单建索引。什么是低基数?比如gender(就男/女),status(就几种状态)。这种字段区分度太差,走索引和全表扫描可能差不了多少,还白占资源。它只适合在复合索引里作为前缀(E部分)使用,用来快速过滤掉大部分数据。
  2. 小心$or查询{$or: [{a:1}, {b:2}]} 这种查询,MongoDB可能会使用ab各自的独立索引,然后合并结果,但效率往往不如人意。如果$or频繁出现且慢,考虑能否用$in重构,或者……认命地建两个复合索引分别覆盖两种情况。
  3. 覆盖查询(Covered Query)是“神技”。如果你的查询需要返回索引中包含的字段,MongoDB可以直接从索引里取数据,根本不用去碰真实的文档数据(回表),速度飞起。在explain()结果里看到"stage": "PROJECTION_COVERED""indexOnly": true,你就偷着乐吧。
  4. 定期用$indexStats看看索引“热度”。有些索引建了半年,一次都没被用过(accesses.ops为0),这就是纯粹的累赘,果断删掉。数据库也需要“断舍离”。

第四步:实战操作与验证

假设我们锁定了那个订单查询,现在要建索引 { userId: 1, status: 1, createTime: -1 }

// 1. 创建索引
db.orders.createIndex({ userId: 1, status: 1, createTime: -1 })

// 2. 用explain验证是否走索引
db.orders.find({
    userId: "user123",
    status: "completed",
    createTime: { $gt: new Date("2023-10-01") }
}).sort({ createTime: -1 }).explain("executionStats")

重点看输出里的:

  • winningPlan.inputStage.stage: 如果是IXSCAN(索引扫描),恭喜你,索引生效了。
  • executionStats.totalDocsExamined: 检查的文档数。这个数应该远小于集合总数,如果还是很大,说明索引过滤性不够好。
  • executionStats.executionTimeMillis: 执行时间,建索引后应该大幅下降。

最后说点大实话

数据库优化,尤其是索引,是个持续的过程,不是一劳永逸的。业务在变,查询模式也在变。你今天建好的最优索引,下个版本可能就成了拖累。

我的习惯是: 把慢查询监控做成常态化,每周扫一眼。新的慢查询冒头了,就重复上面的“诊断-分析-建索引-验证”流程。

说到底,技术方案最怕“配错”。高防IP再牛,防不住数据库自己慢;服务器配置再高,也扛不住全表扫描。把资源花在刀刃上,从一条正确的索引开始,往往比加钱升配来得实在。

行了,不废话了,赶紧去db.currentOp()看看有没有正在折磨你的慢查询吧。

扫描二维码推送至手机访问。

版权声明:本文由www.ysyg.cn发布,如需转载请注明出处。

本文链接:http://www.ysyg.cn:80/?id=444

“MongoDB出现大量慢查询怎么建索引” 的相关文章

详解高防解析中的地理位置感知算法:针对性屏蔽高风险地区流量

# 别让“精准打击”变成“精准误伤”:聊聊高防里的地理位置屏蔽 先说句大实话:很多安全团队,一遇到DDoS攻击,第一反应就是“把海外流量都给我禁了”。这感觉就像家里进了几只苍蝇,你反手把全屋窗户都封死——攻击是拦住了,可正常业务也差不多凉了。 我自己看…

分析高防系统中的节点失效检测算法与秒级流量平滑迁移逻辑

# 高防“后厨”的秘密:当节点挂了,流量怎么做到“丝滑”换桌? 前阵子帮一个做电商的朋友看他们家的高防配置,聊到一半,他突发奇想问了个挺有意思的问题:“你说,你们整天讲高防IP、高防CDN防护多牛,万一你们自己的防护节点突然宕机了,我的业务是不是直接就‘…

分析高防CDN中的重传校验算法如何破解TCP半连接攻击

# 高防CDN里的“暗门”:重传校验算法如何让TCP半连接攻击失效? 我前两天跟一个做游戏的朋友聊天,他愁眉苦脸地说:“上了高防,怎么感觉还是有点卡?攻击一来,服务器还是半死不活的。” 我让他把后台日志拉出来一看,好家伙,满屏的SYN包,典型的TCP半连…

详解高防 CDN 应对大规模静态资源盗刷的流量保护与计费对策

# 静态资源被盗刷?高防CDN的“守财”实战手册 做网站运营的,最怕什么?不是黑客正面硬刚,而是那种悄无声息、温水煮青蛙式的**静态资源盗刷**。 我自己就见过一个挺惨的案例。一个做在线教育的朋友,某天早上起来发现云存储的账单直接爆了,费用是平时月费的…

探讨高防 CDN 接入后出现 504 Gateway Timeout 的技术排查流程

# 高防CDN一上,网站反而504了?别慌,老司机带你一步步“破案” 我前两天刚帮一个做电商的朋友处理了个棘手的故障。他兴冲冲地接入了某家大厂的高防CDN,想着从此可以高枕无忧,不怕打也不怕卡。结果上线当天,后台就炸了——用户时不时就刷出个**504 G…

解析在线教育平台在高峰期遭遇 DDoS 攻击时的 CDN 防御与加速策略

# 当网课卡成PPT:在线教育平台如何扛住“开学季”的流量暴击与恶意攻击? 开学第一周,你精心准备的直播课刚开了十分钟,弹幕就开始刷“老师你卡了”、“声音断断续续”。你心里一紧,检查了自家网络没问题,后台技术团队的电话瞬间被打爆——不是你的问题,是整个平…