9

感觉 aiomysql,异步执行多个查询,性能并没有显著的提升啊

 2 years ago
source link: https://www.v2ex.com/t/790872
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

V2EX  ›  Python

感觉 aiomysql,异步执行多个查询,性能并没有显著的提升啊

  ErenJaeger · 6 小时 45 分钟前 · 746 次点击

代码如下:

 async def query(self, query, param=None):
        conn, cur = await self.getCursor()
        try:
            await cur.execute(query, param)
            return await cur.fetchall()
        except:
            print('error')
        finally:
            if cur:
                await cur.close()
            await self.pool.release(conn)

# mysqlobj 是 aiomysql 连接池对象

result = await asyncio.gather(mysqlobj.query(sql1), mysqlobj.query(sql2), mysqlobj.query(sql3), mysqlobj.query(sql4), mysqlobj.query(sql5))

在我的理解中,如果异步执行的话,这段代码执行的时长应该是这 5 个 sql 中耗时最长的时长,但是测试多次,相对同步执行这 5 个 sql 来说,执行时间并没有显著的提升。各位大佬能指点一下吗?

29 条回复    2021-07-21 19:45:01 +08:00

BBCCBB

BBCCBB   6 小时 28 分钟前

你这代码逻辑上还是`同步`的, 因为你有 await, 会等待每个 sql 执行完成, 就是说一个 sql 执行完后才会去执行下一步的 sql. 只是你这个 sql 还没执行完的过程中, 线程不会卡在这里,, 而是会去执行其他的异步 task. 等你 await 的 task 返回后再继续执行你这个 task.

异步不是你理解的这样的异步. asyncio 的好处是异步 io 的并发..

chaleaoch

chaleaoch   6 小时 21 分钟前

同一个 async 中的 await 是顺序执行的要不然不乱套了吗?

ErenJaeger

ErenJaeger   6 小时 10 分钟前

@BBCCBB 你的意思是 await asyncio.gather()中执行的 sql,还是会顺序执行下去?而不是同时进行查询,查询完毕后返回? 之前写 node.js 的时候,后续处理的步骤都写在回调过程中了,耗时操作不会阻塞,而是去执行下面的步骤了。

ErenJaeger

ErenJaeger   6 小时 7 分钟前

@chaleaoch await 语法糖的作用应该就是等待异步代码返回结果了吧,类似于回调。我想问的是 asyncio.gather()里面的任务不应该是并发执行的吗

chaleaoch

chaleaoch   5 小时 34 分钟前

@ErenJaeger 是并发的没错, 但是 是 task 并发.
协程的基础是生成器 - - 如果从生成器开始看的话 应该会有更好的理解- -

v2exblog

v2exblog   5 小时 30 分钟前

tasks = [asyncio.create_task(mysqlobj.query(sql1)),asyncio.create_task(mysqlobj.query(sql2)),asyncio.create_task(mysqlobj.query(sql3)),asyncio.create_task(mysqlobj.query(sql4)),asyncio.create_task(mysqlobj.query(sql5))]

await asyncio.wait(tasks)
这样试试呢

BBCCBB

BBCCBB   5 小时 25 分钟前

nodejs 继续执行后面是因为你不用 await nodejs 的这个 promise, 而是等他返回结果返回后, 调用你注册的回调 function.去处理 function 果返回

nodejs 里, 如果你后续的执行依赖某个 nodejs 异步函数的结果, 那不还是得等异步函数结束拿到结果后再执行后面的结果吗.


1:
var s = asyncFunc(xxx, function (res) {
res 是这个函数的返回值.
}); // 不需要等待 asyncFunc 返回值,

xxx(); //


2:
var res = await asyncFunc(xxx); // 需要等待 asyncFunc 返回值
console.log(res)
xxx()

类似这两种方式.

pabupa

pabupa   5 小时 17 分钟前

楼主这种用法没有问题。

pabupa

pabupa   5 小时 15 分钟前

我觉得应该是驱动的问题,,,

pabupa

pabupa   5 小时 15 分钟前

@pabupa 比如你的连接池中的连接可能太小了……

ErenJaeger

ErenJaeger   5 小时 8 分钟前

@chaleaoch 是呀,就是我指的就是 gather 里面的任务并发执行的话,执行时长应该是这里面最长执行任务的时长,而不是所有任务执行时长的累积

Vegetable

Vegetable   5 小时 5 分钟前

用法没问题,不过信息还是不够。包括链接池大小和具体时常。甚至说,mysql 的性能是不是瓶颈,都需要考虑。

ErenJaeger

ErenJaeger   5 小时 1 分钟前

@BBCCBB 是的,这两种方式是写法的区别。 我的意思是:
fuction outside(){
asyncFucn1()
asyncFunc2()
asyncFunc3()
asycnFunc4()
......
}
如果 outside 函数等待里面异步函数结束退出的话,执行时长应该是内部异步函数执行时间最长的时长吧。那我同时发起 N 个 sql 查询,整体查询时长应该是 N 个查询 sql 中执行时长最长的时长吧

ErenJaeger

ErenJaeger   5 小时 0 分钟前

@pabupa mix 是 5,max 是 10,按理说应该够了,我调整一下试试

ErenJaeger

ErenJaeger   4 小时 59 分钟前

@v2exblog 试了试,差不多,timeit 测试了下跟同步查询的差不多,就感觉很奇怪

chaleaoch

chaleaoch   4 小时 57 分钟前

@ErenJaeger 换成 100 次查询试一下.

BBCCBB

BBCCBB   4 小时 48 分钟前

sorry, 理解错了.. 光看了你问题里写的 5 个 sql, 我漏看了下面的 gather, 你要问的是 gather 里的这 5 个 task 吧

BBCCBB

BBCCBB   4 小时 47 分钟前

刚好你这个 task 里也是 5 个 sql 的 await.. :(

ErenJaeger

ErenJaeger   4 小时 45 分钟前

@chaleaoch 淦,我调了 1000 次,同步 40 多秒,异步 5 秒多。问题生产环境中一个接口里面不可能会有这么高频次的查询,小频次的查询,同步异步的差距就很不明显

BBCCBB

BBCCBB   4 小时 45 分钟前

次数调大点试试. 看起来没啥问题. 可以把代码贴完整点, 包括 loop.run_until_complete()这一块

BBCCBB

BBCCBB   4 小时 42 分钟前

一个接口不可能这么高, 但是其他的接口也有网络 io, 和你测一个接口 1000 次概念差不多, 并发上去了, asyncio 性能差异就出来了..

如果只是一个接口, 没啥访问, 直接同步搞.. 简单

ErenJaeger

ErenJaeger   4 小时 42 分钟前

@BBCCBB gather 里面是顺序执行的吗?我看官方文档里是这样描述的:
同时运行 aws 序列中的可等待对象。

如果 aws 中的任何 awaitable 是协程,则它会自动安排为任务。

如果所有 awaitable 都成功完成,则结果是返回值的聚合列表。结果值的顺序对应于 aws 中等待的顺序。

如果 return_exceptions 为 False (默认),第一个引发的异常会立即传播到在 gather() 上等待的任务。aws 序列中的其他等待对象不会被取消,而是会继续运行。

如果 return_exceptions 为 True,则将异常视为成功结果,并在结果列表中聚合。

如果 gather() 被取消,所有提交的等待(尚未完成)也将被取消。

如果 aws 序列中的任何 Task 或 Future 被取消,则将其视为引发了 CancelledError - 在这种情况下不会取消 gather() 调用。这是为了防止取消一个提交的任务 /未来导致其他任务 /未来被取消。

ErenJaeger

ErenJaeger   4 小时 37 分钟前

class Pmysql:

def __init__(self):
self.conn = None
self.pool = None

async def initpool(self):
try:
__pool = await aiomysql.create_pool(minsize=10,
maxsize=10,
host=Config.host,
port=Config.port,
user=Config.user,
password=Config.password,
db='db')
return __pool
except Exception as e:
print(e)
print('create connect error.')

async def getCursor(self):
conn = await self.pool.acquire()
cur = await conn.cursor()
return conn, cur

async def query(self, query, param=None):
conn, cur = await self.getCursor()
try:
await cur.execute(query, param)
return await cur.fetchall()
except:
print('error')
finally:
if cur:
await cur.close()
await self.pool.release(conn)

async def getAmysqlobj():
mysqlobj = Pmysql()
pool = await mysqlobj.initpool()
mysqlobj.pool = pool
return mysqlobj

BBCCBB

BBCCBB   4 小时 29 分钟前

gather 不是顺序执行, 都 asyncio 了, 只要里面 task 不阻塞, 就是异步执行的.

你这个没啥问题, 根据你加大到 1000 次, 差距挺大的, 所以应该是量不够大.

ErenJaeger

ErenJaeger   4 小时 23 分钟前

@BBCCBB 其实最大的需求,还是提升响应效率了,这个查询 2s 多能降到 1s 多,甚至不到 1s,就是最高的期望值了,但是测试感觉难以实现

BBCCBB

BBCCBB   4 小时 12 分钟前

asyncio 不能降低你代码里单个 query 方法的耗时, 他要做的是用少量线程就能支撑超高的并发量,, 这个用线程是很难实现的, 单个请求的响应时间并不会变得更快,

vindurriel

vindurriel   3 小时 12 分钟前 via iPhone

sql 是啥 可以换成 sleep(1) 看是 n 秒返回还是 1 秒多返回

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK