90%的性能问题源于find()未配索引、缺失投影和延迟过滤。必须用explain("executionstats")查看totaldocsexamined与nreturned差距、避免collscan;投影精简字段、$match前置、复合索引按等值在前/范围在后排序,并确认索引真实生效。

直接上结论:90% 的性能问题出在 find() 没配索引、没写投影、没早过滤,而不是 Python 本身慢。
为什么 find() 越跑越慢?先看执行计划
不查 explain('executionStats') 就调优,等于蒙眼修车。MongoDB 不会主动告诉你它扫了几百万文档——除非你问。
-
executionStats.nReturned是真正返回的文档数,nDocsExamined是它实际扫描的文档数;两者差距越大,说明索引越没用好 - 如果看到
stage: "COLLSCAN",就是全表扫描,立刻停手,别再测接口耗时了 - 注意
executionTimeMillis是服务端执行时间,不含网络传输和 Python 解析开销,但它是瓶颈定位的黄金指标
find() 查询必须带投影和条件过滤
默认返回整个文档,哪怕你只用其中 1 个字段。800 万条数据里每个文档 5KB,光网络传输就压垮带宽。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 用投影显式指定字段:
db.collection.find({"status": "active"}, {"name": 1, "updated_at": 1, "_id": 0}),避免传回大字段如Actions数组(参考 800 万条案例中那个 260 元素 JSON) -
$match必须放在聚合管道最前面,$project尽量靠前;find()同理,条件越具体、越早写死越好,别依赖 Python 循环里二次过滤 - 避免在 Python 里做
for doc in cursor: if doc['x'] > 100: ...—— 这类逻辑必须下推到数据库层
复合索引顺序不是“把所有查询字段堆一起”
字段顺序决定索引是否生效。MongoDB 只能高效使用符合“前缀匹配”的部分。
- 比如查询
{"user_id": 123, "created_at": {"$gt": "2025-01-01"}},索引应建为db.collection.create_index([("user_id", 1), ("created_at", -1)]),反过来就失效 - 区分度高的字段放前面:
user_id(唯一或高基数)比status(只有几个枚举值)更适合当索引首字段 - 用
getIndexes()确认索引已建成功,别信“我昨天建过了”——有时连接错库、集合名拼错、或权限不足导致静默失败
aggregate() 大数据量下必须开 allow_disk_use
内存不够时,默认聚合会直接报错 errmsg: "Sort exceeded memory limit",而不是自动落盘。
- 只要管道里有
$sort或$group且数据量预估超几十万,就必须加allow_disk_use=True - 注意:这会写临时文件到 MongoDB 配置的
dbPath下的_tmp目录,确保磁盘空间充足且 IO 不是瓶颈 - 更治本的办法是提前用
$match过滤掉 95% 数据,再进聚合——很多慢聚合其实卡在第一步没过滤
真正难的不是写对语法,而是判断哪一步该下推、哪一步该拆开、哪个字段值得建索引。线上查 explain 一次,比本地压测十次都管用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










