为什么 MongoDB 的 python motor 客户端在使用 starlette 运行时比 pymongo 慢很多?

Bo *_*ker 11 mongodb pymongo python-asyncio

我有一个用 python 和异步框架 Starlette ( https://www.starlette.io/ ) 编写的 Web 应用程序,我需要连接到 MongoDB,所以我安装了异步 pymongo 驱动程序: motor ( https://motor.readthedocs .io/en/stable/)版本 2.1.0

我有一些像这样的伪代码:


motor_client = motor.motor_asyncio.AsyncIOMotorClient(...)
pymongo_client = pymongo.MongoClient(...)

class BlogList(HTTPEndpoint):
    async def get(self, request):
        blog_list = [item async for item in motor_client.blog.find(condition)]
        # blog_list = list(pymongo_client.blog.find(condition))
        data = {
            "request": request,
            "blogs": blog_list,
        }
        return RenderPageResponse("index.html", data)
Run Code Online (Sandbox Code Playgroud)

对于blog_list这里的行,我用以下方法对其进行了基准测试wrk -t10 -d10 -c100 http://localhost:8080/blog:

  • 如果我使用motor_clientwith await,结果约为“1500 个请求/秒”
  • 如果我将其更改为pymongo_client(同步模式),结果约为“1900 个请求/秒”

使用电机的异步模式不应该比使用 pymongo 的同步模式更快吗?我想知道为什么使用 motor(异步,1500 req/s)时的性能比 pymongo(sync,1900 req/s)差得多?

其他信息:我使用 运行我的 starlette 应用程序gunicorn -w 4 -k uvicorn.workers.UvicornWorker myblog:app,只需切换这两行即可进行wrk基准测试。

我进行了搜索,但找不到谈论此问题的帖子,所以我想知道我错过了什么吗?任何帮助将不胜感激,谢谢。

Nes*_*vij 11

也许问题是由于电机实际上不是异步驱动器。它只是在ThreadPoolExecutor中启动同步pymongo,因此性能下降。不幸的是,目前 python 上没有 mongo 的异步驱动程序:(


小智 9

我使用fastapi对此做了一些实验
首先将1515个文档添加到mongodb中的集合中
然后定义3个测试用例
测试用例1
使用pymongo从同步函数从数据库中获取文档

@app.get("/get_data")
def read_data():
    cursor  = coll.find({})
    li = []
    for rec in cursor:
        li.append(rec)
    length = len(li)
    return {"length": length}
Run Code Online (Sandbox Code Playgroud)

测试用例 2
使用pymongo从异步函数中从数据库获取文档

@app.get("/get_data")
async def read_data():
    cursor  = coll.find({})
    li = []
    for rec in cursor:
        li.append(rec)
    length = len(li)
    return {"length": length}
Run Code Online (Sandbox Code Playgroud)

测试用例 3
使用motor从异步函数中从数据库获取文档

@app.get("/get_data")
async def read_data():
    cursor  = coll.find({})
    li = []
    async for document in cursor:
        li.append(document)
    return {"length": len(li)}
Run Code Online (Sandbox Code Playgroud)

使用 uvicorn 运行具有 5 个工作人员的应用程序
uvicorn test:app --host 0.0.0.0 --port 8000 --workers 5

使用Locust来测试
代码:

class HelloWorldUser(HttpUser):
    @task
    def hello_world(self):
        self.client.get("/get_data")
Run Code Online (Sandbox Code Playgroud)

配置:
模拟的总用户数= 50
生成率(每秒)= 10

结果

测试案例1

 {
   "Type":["GET"],
   "Name":["/get_data"],
   "Request Count":[1546],
   "Failure Count":[0],
   "Median Response Time":[1300],
   "Average Response Time":["1409.07469337902"],
   "Min Response Time":["213.365600000543"],
   "Max Response Time":["6493.73753599866"],
   "Average Content Size":[15],
   "Requests/s":["32.9464475484361"],
   "Failures/s":[0],
   "50%":[1300],
   "66%":[1500],
   "75%":[1700],
   "80%":[1900],
   "90%":[2300],
   "95%":[2700],
   "98%":[3200],
   "99%":[3600],
   "99.9%":[5200],
   "99.99%":[6500],
   "100%":[6500]
}
Run Code Online (Sandbox Code Playgroud)

测试案例2

{
   "Type":["GET"],
   "Name":["/get_data"],
   "Request Count":[1586],
   "Failure Count":[0],
   "Median Response Time":[2000],
   "Average Response Time":["2108.97265052965"],
   "Min Response Time":["261.412365001888"],
   "Max Response Time":["5301.57826199866"],
   "Average Content Size":[15],
   "Requests/s":["22.4672730359705"],
   "Failures/s":[0],
   "50%":[2000],
   "66%":[2400],
   "75%":[2500],
   "80%":[2700],
   "90%":[2900],
   "95%":[3300],
   "98%":[3600],
   "99%":[4100],
   "99.9%":[5100],
   "99.99%":[5300],
   "100%":[5300]
}
Run Code Online (Sandbox Code Playgroud)

测试用例3

{
   "Type":["GET"],
   "Name":["/get_data"],
   "Request Count":[1662],
   "Failure Count":[0],
   "Median Response Time":[1200],
   "Average Response Time":["1388.38606281409"],
   "Min Response Time":["116.913538000517"],
   "Max Response Time":["7983.01199100024"],
   "Average Content Size":[15],
   "Requests/s":["33.5720588256683"],
   "Failures/s":[0],
   "50%":[1200],
   "66%":[1500],
   "75%":[1700],
   "80%":[1900],
   "90%":[2300],
   "95%":[2700],
   "98%":[3400],
   "99%":[4100],
   "99.9%":[6800],
   "99.99%":[8000],
   "100%":[8000]
}
Run Code Online (Sandbox Code Playgroud)