子进程复用父进程MongoClient会引发死锁和连接混乱,因MongoClient非进程安全;每个子进程必须独立创建实例,并显式调用close()释放资源,合理设置connectTimeoutMS、serverSelectionTimeoutMS及maxPoolSize。

子进程复用父进程MongoClient对象会引发连接状态混乱
PyMongo的MongoClient不是进程安全的,它内部维护着连接池、心跳线程和socket状态。当父进程创建的client被直接传入子进程(比如通过args或全局变量),子进程会继承一个已损坏的文件描述符副本——操作系统不允许跨进程共享TCP连接,结果是所有子进程在首次调用insert_one()时卡住,最终触发ServerSelectionTimeoutError或ConnectionTimeoutError。
常见错误写法:pool.map(worker, data_list, client=parent_client) 或在模块顶层初始化client = MongoClient(...)后直接在子进程中调用。
- 每个子进程必须在函数内部新建
MongoClient,不能复用任何外部引用 - 不要在
if __name__ == "__main__":外初始化客户端,避免被子进程重复导入执行 - 若用
concurrent.futures.ProcessPoolExecutor,确保initializer不传递client,只做环境准备
未显式关闭连接导致socket耗尽
子进程退出时,Python解释器不保证自动回收MongoClient持有的socket。尤其在高频启停进程(如每秒启动数十个子进程)时,大量TIME_WAIT状态连接堆积,很快占满本地端口范围,新连接因“Cannot assign requested address”失败,表现为ConnectionRefusedError或无响应超时。
关键点:client.close()必须在try/finally中显式调用,不能依赖垃圾回收。
立即学习“Python免费学习笔记(深入)”;
- 在子进程函数末尾加
finally: client.close(),哪怕发生异常也要释放资源 - 避免在
with语句中使用MongoClient——它没有实现__enter__/__exit__ - 检查系统
net.ipv4.ip_local_port_range,默认可能只有32768–65535,不够并发场景用
连接参数未适配多进程场景
默认的connectTimeoutMS=20000和serverSelectionTimeoutMS=30000在多进程下放大了失败概率:10个子进程同时发起连接,若其中2个因网络抖动延迟,其余8个会在30秒内持续轮询节点列表,拖慢整体进度并挤占DNS/TCP资源。
更糟的是,如果MongoDB地址含DNS名(如mongodb://mycluster.example.com),所有子进程会并发触发DNS解析,可能触发DNS服务器限流,进一步延长serverSelectionTimeoutMS内的等待时间。
- 统一设为较小值,例如
connectTimeoutMS=5000和serverSelectionTimeoutMS=5000 - 加
directConnection=True跳过服务发现(单机或明确IP部署时有效) - 禁用SRV解析:
MongoClient(uri, srv=False),防止DNS SRV记录覆盖URI参数
连接池配置不合理加剧竞争
每个子进程默认创建独立连接池(maxPoolSize=100),10个子进程就可能占用上千连接。MongoDB服务端有maxIncomingConnections限制(默认65536,但实际受内存和文件描述符约束),超出后新连接被拒绝,现象就是随机超时。
这不是客户端“连不上”,而是服务端主动丢包,日志里常看不到明显错误,只能看到客户端等满connectTimeoutMS后报错。
- 显式限制子进程连接池大小:
MongoClient(..., maxPoolSize=5, minPoolSize=1) - 避免设置
maxIdleTimeMS过小(如 - 确认MongoDB服务端
ulimit -n足够高,建议≥65536
client.close()没写进finally块,以及DNS解析在多个进程间未缓存复用。


















