CursorNotFound错误本质是MongoDB服务端在游标空闲10分钟后自动清理所致,非网络或客户端故障;启用no_cursor_timeout=True可绕过时限但需显式关闭游标以防资源耗尽,推荐分页查询或_id范围扫描替代长生命周期游标。

CursorNotFound 错误本质是服务端游标被自动清理了
MongoDB 默认在游标空闲 10 分钟后主动销毁它,不是网络断开或客户端崩溃导致的——只要你的 cursor 拿到结果后没及时遍历完、又没做任何操作,服务端就当“不用了”,直接回收。这时候再调 next() 或继续 for 循环,就会抛 CursorNotFound。
no_cursor_timeout=True 不是万能续命开关
加这个参数确实能跳过 10 分钟限制,但代价明显:服务端会一直保留游标资源,直到你显式关闭(cursor.close())或连接断开。长期不关会导致内存堆积、句柄耗尽,尤其在高并发或长周期数据导出场景下容易拖垮 mongod。
- 只应在明确需要长时间分批处理时启用,比如后台导出百万级文档、ETL 脚本
- 必须配
try/finally或上下文管理器确保cursor.close()执行 - PyMongo 4.0+ 中,
find()返回的Cursor不再支持直接传no_cursor_timeout=True,得用cursor_type=CursorType.EXHAUST或改用allow_disk_use=True配合批量读取 - 示例写法:
cursor = collection.find({“status”: “pending”}, no_cursor_timeout=True)(PyMongo < 4.0);新版建议:cursor = collection.find({“status”: “pending”}, cursor_type=CursorType.NON_TAILABLE)并靠应用层控制拉取节奏
更稳妥的做法是主动分页 + 限定单次拉取量
比起让游标永生,不如让它短命但高频刷新。用 skip()+limit() 或基于 _id 的范围查询,每次只取固定批次,避免单次游标挂太久。
-
limit(1000)是底线,别用默认不限量;配合batch_size参数(如batch_size=500)让驱动分批从服务端取数据,减少单次网络压力 - 用
find().sort(“_id”).limit(1000)查第一批,记下最后一条的_id,下次查{“_id”: {“$gt”: last_id}},比skip()效率高得多 - 如果业务允许,把大查询拆成多个小查询,比如按时间字段分天查,每查一天就关掉当前游标
调试时别只盯着 cursor,先确认是不是网络或连接池问题
有些 CursorNotFound 看似是超时,实则是连接被中间设备(如负载均衡、防火墙)静默断开,或者 PyMongo 连接池里混进了失效连接。这类问题加 no_cursor_timeout=True 也没用。
立即学习“Python免费学习笔记(深入)”;
- 检查 MongoDB 日志里有没有
connection reset或client disconnected相关记录 - 在
find()前加collection.database.client.server_info()验证连接是否还活着 - 设置连接选项:
client = MongoClient(…, socketTimeoutMS=30000, connectTimeoutMS=20000)
,避免卡在建连阶段掩盖真实问题

















