PHP前端开发

如何在 Python 中异步操作数据库?

百变鹏仔 1个月前 (01-21) #Python
文章标签 操作


Python 目前已经进化到了 3.8 版本,对操作数据库也提供了相应的异步支持。当我们做一个 Web 服务时,性能的瓶颈绝大部分都在数据库上,如果一个请求从数据库中读数据的时候能够自动切换、去处理其它请求的话,是不是就能提高并发量了呢。

(编者注:原文写于 2020 年 2 月,当时最新为Python 3.8,文章内容现在仍未过时)

下面我们来看看如何使用 Python 异步操作 MySQL、PostgreSQL 以及 Redis,以上几个可以说是最常用的数据库了。至于 SQLServer、Oracle,本人没有找到相应的异步驱动,有兴趣可以自己去探索一下。

而操作数据库无非就是增删改查,下面我们来看看如何异步实现它们。

异步操作 MySQL

异步操作 MySQL 的话,需要使用一个 aiomysql,直接 pip install aiomysql 即可。

aiomysql 底层依赖于 pymysql,所以 aiomysql 并没有单独实现相应的连接驱动,而是在 pymysql 之上进行了封装。

查询记录

下面先来看看如何查询记录。

import asyncioimport aiomysql.sa as aio_saasync def main():# 创建一个异步引擎engine = await aio_sa.create_engine(host="xx.xxx.xx.xxx",port=3306,user="root",password="root",db="_hanser",connect_timeout=10)# 通过 engine.acquire() 获取一个连接async with engine.acquire() as conn:# 异步执行, 返回一个 <class> 对象result = await conn.execute("SELECT * FROM girl")# 通过 await result.fetchone() 可以获取满足条件的第一条记录, 一个 <class> 对象data = await result.fetchone()# 可以将 <class> 对象想象成一个字典print(data.keys())# KeysView((1, '古明地觉', 16, '地灵殿'))print(list(data.keys()))# ['id', 'name', 'age', 'place']print(data.values())# ValuesView((1, '古明地觉', 16, '地灵殿'))print(list(data.values()))# [1, '古明地觉', 16, '地灵殿']print(data.items())# ItemsView((1, '古明地觉', 16, '地灵殿'))print(list(data.items()))# [('id', 1), ('name', '古明地觉'), ('age', 16), ('place', '地灵殿')]# 直接转成字典也是可以的print(dict(data))# {'id': 1, 'name': '古明地觉', 'age': 16, 'place': '地灵殿'}# 最后别忘记关闭引擎, 当然你在创建引擎的时候也可以通过 async with aio_sa.create_engine 的方式创建# async with 语句结束后会自动执行下面两行代码engine.close()await engine.wait_closed() loop = asyncio.get_event_loop()loop.run_until_complete(main())loop.close()</class></class></class>

怎么样,是不是很简单呢,和同步库的操作方式其实是类似的。但是很明显,我们在获取记录的时候不会只获取一条,而是会获取多条,获取多条的话使用 await result.fetchall() 即可。

import asynciofrom pprint import pprintimport aiomysql.sa as aio_saasync def main():# 通过异步上下文管理器的方式创建, 会自动帮我们关闭引擎async with aio_sa.create_engine(host="xx.xxx.xx.xxx",port=3306,user="root",password="root",db="_hanser",connect_timeout=10) as engine:async with engine.acquire() as conn:result = await conn.execute("SELECT * FROM girl")# 此时的 data 是一个列表, 列表里面是 <class> 对象data = await result.fetchall()# 将里面的元素转成字典pprint(list(map(dict, data)))"""[{'age': 16, 'id': 1, 'name': '古明地觉', 'place': '地灵殿'}, {'age': 16, 'id': 2, 'name': '雾雨魔理沙', 'place': '魔法森林'}, {'age': 400, 'id': 3, 'name': '芙兰朵露', 'place': '红魔馆'}]"""loop = asyncio.get_event_loop()loop.run_until_complete(main())loop.close()</class>

除了 fetchone、fetchall 之外,还有一个 fetchmany,可以获取指定记录的条数。

import asynciofrom pprint import pprintimport aiomysql.sa as aio_saasync def main():# 通过异步上下文管理器的方式创建, 会自动帮我们关闭引擎async with aio_sa.create_engine(host="xx.xxx.xx.xxx",port=3306,user="root",password="root",db="_hanser",connect_timeout=10) as engine:async with engine.acquire() as conn:result = await conn.execute("SELECT * FROM girl")# 默认是获取一条, 得到的仍然是一个列表data = await result.fetchmany(2)pprint(list(map(dict, data)))"""[{'age': 16, 'id': 1, 'name': '古明地觉', 'place': '地灵殿'}, {'age': 16, 'id': 2, 'name': '雾雨魔理沙', 'place': '魔法森林'}]"""loop = asyncio.get_event_loop()loop.run_until_complete(main())loop.close()