noCursorTimeout=True不能防止会话超时,仅避免游标空闲10分钟被销毁;会话默认30分钟过期,过期即终止关联游标并报CursorNotFound;batch_size()通过分批请求重置计时器更稳妥。

noCursorTimeout=True 不能代替会话管理
设 noCursorTimeout=True 只是让游标不因空闲 10 分钟被关(cursorTimeoutMillis 默认值),但它对会话超时完全无效。MongoDB 默认会话生命周期是 30 分钟(localLogicalSessionTimeoutMinutes),一旦过期,所有绑定该会话的游标都会被服务器静默销毁,报 CursorNotFound。哪怕你每秒都在取数据,只要会话本身到期了,照样失败。
常见错误现象:pymongo.errors.CursorNotFound: cursor id 123456789 not found 出现在长循环中调用外部 API 或写磁盘等慢操作之后——不是游标空闲了,是会话死了。
- Atlas M0/Flex 集群直接忽略
noCursorTimeout,设了也白设 - 单条文档处理耗时 >30 秒时,
noCursorTimeout=True已经失去意义 - 不要依赖修改全局
cursorTimeoutMillis:需重启 mongod,生产环境基本不可行
batch_size() 是更实际的“分段续命”方案
batch_size() 不是延长超时,而是把一次长迭代切分成多个短请求。每次拉完一批(比如 5000 条),驱动自动发新请求拿下一批,相当于重置空闲计时器。只要单批处理时间
实操建议:
- 从
batch_size(1000)起手,用日志测单批平均耗时;若稳定在 30 秒内,可逐步加到5000或10000 - 必须和
no_cursor_timeout=True一起用:collection.find({}, no_cursor_timeout=True).batch_size(5000) - 避免设
batch_size(100000):单批内存暴涨,且一旦卡住仍会超时,OOM 风险高
长时间任务必须显式管理会话生命周期
跑几小时的数据迁移、ETL 或离线清洗,光靠 no_cursor_timeout 和 batch_size 不够。你需要自己握着会话命脉——显式创建、定期刷新、异常清理。
关键动作:
- 用
client.start_session()创建会话,再把查询绑定进去:with client.start_session() as session: collection.find(..., session=session) - 每 25 分钟左右调一次
client.admin.command("refreshSessions", [session.session_id])(注意不是直接执行refreshSessions命令) - 别硬编码会话 ID,从
session.session_id动态取 - 任何异常路径都必须调
session.end_session(),否则残留会话会持续占用连接和内存
max_time_ms 是防查询卡死,不是防游标超时
max_time_ms 控制的是单次查询(如 find 或聚合)在服务器端的**执行耗时上限**,超时后 MongoDB 主动中止查询并返回错误。它和游标生命周期无关,也不会影响 cursorTimeoutMillis 或 localLogicalSessionTimeoutMinutes。
典型误用:
- 以为设了
max_time_ms=300000就能跑通 5 分钟的单条处理逻辑——错,它只管“查得有多快”,不管“查完后你啥时候来取下一批” - 在长循环里对每个
find都加max_time_ms,但没配batch_size,结果第 101 批还是CursorNotFound - 和
no_cursor_timeout=True混用时没意识到:前者保查询不 hang,后者保游标不 idle,两者职责完全不同
真正容易被忽略的是会话刷新时机——25 分钟不是拍脑袋定的,它要留出网络延迟和命令执行缓冲,早于 30 分钟阈值触发,才不会踩到临界点。











