asyncio.run()不能直接套用Motor连接实例,因client.close()可能被丢弃或在事件循环关闭后执行;正确做法是用async with确保资源释放,并配合连接池调优、批量写入和Semaphore并发控制。

asyncio.run() 不能直接套用 motor 的连接实例
Motor 是 asyncio 生态里最常用的 MongoDB 异步驱动,但它本身不依赖 asyncio.run() 启动事件循环——而是要求你显式管理连接生命周期。常见错误是写成:
async def main():
client = motor.motor_asyncio.AsyncIOMotorClient("mongodb://localhost:27017")
await client.test_db.test_collection.insert_one({"x": 1})
client.close()
<p>asyncio.run(main()) # ❌ 危险:client.close() 可能被丢弃或在 loop 关闭后执行正确做法是用 async with 确保资源释放:
- 始终用
async with motor.motor_asyncio.AsyncIOMotorClient(...) as client: - 数据库和集合也推荐用
async with(虽非强制,但语义更清晰) - 避免在
asyncio.run()外部保留 client 实例,尤其不要跨协程复用未加锁的连接对象
批量写入必须用 insert_many() 而非循环调用 insert_one()
并发写入时,如果对每条文档都发一个 insert_one(),哪怕用了 asyncio.gather(),也会因网络往返叠加导致吞吐骤降。MongoDB 服务端对单次批量操作有显著优化。
立即学习“Python免费学习笔记(深入)”;
典型错误写法:
await asyncio.gather(*[coll.insert_one(doc) for doc in docs]) # ❌ 每个 insert_one 都是独立 round-trip
应改为:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 按需分批,例如每 100–500 条一组调用
insert_many(docs_batch) - 设置
bypass_document_validation=True(如已确保数据格式)可省去服务端校验开销 - 若需插入后获取
_id,注意insert_many()返回的是InsertManyResult,含inserted_ids列表,不是单个 ID
高并发下连接池耗尽常表现为 TimeoutError 或 ConnectionResetError
MongoDB 默认连接池大小是 100,但在 Python 3.12 + asyncio 高并发场景中,如果协程数远超此值(比如启动 500 个并发写任务),部分请求会卡在连接获取阶段,最终超时。错误信息类似:
TimeoutError: Connection timeout while waiting for connection from pool
解决路径很直接:
- 初始化 client 时显式加大连接池:
maxPoolSize=500(根据服务器内存和 mongod 并发能力调整) - 同时设
minPoolSize=10避免冷启动延迟 - 禁用连接自动重试(
retryWrites=False)可减少意外阻塞,尤其在纯写入场景 - 确认 mongod 配置中的
net.maxIncomingConnections足够(默认 65536,一般够用)
使用 asyncio.Semaphore 控制并发粒度比盲目开满更稳
即使调大了连接池,也不代表应该让所有协程同时冲向 MongoDB。网络、序列化、服务端锁竞争都会成为瓶颈。硬扛 1000 并发写入往往不如 50 并发 + 批量写入快。
推荐模式:
- 用
sem = asyncio.Semaphore(32)限制同时活跃的写任务数 - 每个任务内部再做
insert_many()批处理(如每批 200 条) - 避免把
sem放在insert_many()外层锁住整个批处理——那等于串行化,失去并发意义 - 真正要锁的是“获取连接 + 发起写请求”这一段,即
await sem.acquire()应紧贴await coll.insert_many(...)
连接池、批处理、信号量三层控制叠在一起,才是 Python 3.12 下稳定压测 MongoDB 的实际落点。漏掉任意一层,都可能在 1k+ QPS 时突然抖动或失败。

















