FastAPI中pymongo同步路由比motor异步路由慢太多?求排查
FastAPI异步路由+Motor性能远低于同步路由+pymongo问题排查
问题场景
使用FastAPI时,参考MongoDB官方推荐采用Motor作为异步驱动,搭建了两个功能完全一致的数据库查询路由:
- 异步路由:使用
AsyncIOMotorClient,接口路径/test - 同步路由:使用
MongoClient,接口路径/test-sync
在300用户(约60请求/秒)的压测下,异步路由平均响应时间达27秒,而同步路由仅需50ms。已尝试调整Docker容器worker数量、增加CPU/内存资源、配置ECS额外任务,但问题未解决。/health接口压测响应正常,排除整体资源瓶颈。
相关代码
db.py
client = AsyncIOMotorClient( mongo_con_string, ) sync_client = MongoClient( mongo_con_string, ) mongo_db = client["test"] sync_mongo_db = sync_client["test"]
views.py
@test_router.get("/test", status_code=200, response_description="") async def get_articles(): collection = mongo_db[TEST_TABLE] articles = await collection.find().to_list(1000) articles_str = json.dumps(articles, default=str) @test_router.get("/test-sync", status_code=200, response_description="") def get_articles_sync(): collection = sync_mongo_db[TEST_TABLE] articles = collection.find().to_list(1000) articles_str = json.dumps(articles, default=str)
Dockerfile
FROM python:3.11 WORKDIR /code COPY ./api/requirements.txt /code/requirements.txt RUN pip install --upgrade -r /code/requirements.txt COPY ./api/app /code/app CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers", "--workers", "10"]
可能的问题原因及解决方案
1. Motor连接池配置不合理
AsyncIOMotorClient默认连接池大小可能远小于同步MongoClient,导致高并发下请求排队等待连接。
- 解决方案:显式配置连接池参数,增加最大连接数:
client = AsyncIOMotorClient( mongo_con_string, maxPoolSize=100, # 调整为适合并发量的数值 minPoolSize=10, maxIdleTimeMS=30000 # 闲置连接超时时间 )
2. 同步JSON序列化阻塞事件循环
json.dumps是同步CPU密集型操作,当序列化1000条文档时,会长时间阻塞异步事件循环,导致后续请求无法处理。而同步路由依赖线程池,每个请求的序列化操作在独立线程中执行,不会互相影响。
- 解决方案:将序列化操作转移到线程池执行,避免阻塞事件循环:
import asyncio import json @test_router.get("/test", status_code=200, response_description="") async def get_articles(): collection = mongo_db[TEST_TABLE] articles = await collection.find().to_list(1000) # 用线程池执行同步序列化 articles_str = await asyncio.get_event_loop().run_in_executor( None, lambda: json.dumps(articles, default=str) ) return articles_str
或者使用更快的序列化库(如orjson、ujson)减少序列化耗时。
3. 事件循环效率问题
默认的asyncio事件循环性能可能不如uvloop,高并发下差异明显。
- 解决方案:安装
uvloop并指定Uvicorn使用该事件循环:
pip install uvloop
修改Dockerfile启动命令:
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers", "--workers", "10", "--loop", "uvloop"]
4. 数据库查询耗时验证
先确认是数据库查询慢还是后续处理慢:在异步路由中添加日志记录查询耗时:
import time @test_router.get("/test", status_code=200, response_description="") async def get_articles(): collection = mongo_db[TEST_TABLE] start = time.time() articles = await collection.find().to_list(1000) print(f"DB query time: {time.time() - start:.2f}s") # ... 后续操作
如果查询耗时本身很长,需要检查MongoDB实例的索引、性能,确认异步客户端和同步客户端的连接配置(如是否使用相同的副本集、读写偏好)一致。
内容的提问来源于stack exchange,提问作者serp002
相关产品推荐
相关产品推荐

