pythonweb异步连接db

编程/Python
[FastAPI] 9.如何异步处理持久层部分
2021. 4. 4. 12:52
在第一篇文章中,我们谈到了 FastAPI 可以使用基于 ASGI 的 uvicorn 与基于 uvloop 的异步处理异步处理 API 请求和响应。遗憾的是,由于Database Connection不支持这些功能,即使API请求端可以进行异步处理,但DB访问部分并没有被异步处理,因此发生阻塞,下一个请求一直在等待。

Python 数据库 API
那么,Python如何连接数据库呢?在 Java 中,有一个名为 JDBC 的数据库连接器,它可以在应用程序和数据库之间进行连接。

在 Python 中,存在与此类似的东西,称为 DB API。

PEP 249 – Python 数据库 API 规范 v2.0
www.python.org
Python 文档 PEP 249 包含 Python DB API 2.0。作为在 Python 中访问 DB 的标准接口,我们使用这些基于标准 API 的模块来访问数据库,即使在我们之前介绍的 FastAPI CRUD 中也是如此。

为了更容易理解,PostgreSQL 有一个名为 asyncpg 的数据库库。这个库是为PostgreSQL量身定做的,以Python DB API为基本骨架,使用Connection对象建立连接,创建Cursor对象编写Query,在连接上使用execute方法执行Query。

这种结构不仅在 asyncpg 中实现,而且在 Python 支持的大多数 DB 库中实现。这是标准的 Python DB API。

(旧)数据库 + asyncpg + SQLAlchemy 1.3
asyncpg 是 PosgreSQL 的异步数据库库,在 FastAPI 中,通过结合 Databases 和 asyncpg 模块,有一种基于 SQLAlchemy Core 的语法来执行异步事务的方法。

asyncpg 基本基于 Python 的 asyncio 运行,从 Python 3.5 开始运行,支持 PostgreSQL 9.2 到 12 版本。
$ poetry add databases[postgresql]
使用Databases依赖时,需要在包类型中放入要使用的数据库名称,并添加到依赖中。如果添加了这个依赖,SQLAlchemy 1.3 版本的依赖也会被添加。

正式地,SQLAlchemy 1.3 版本不支持 asyncpg 方言。因此,为了在FastAPI或Sanic等异步框架中将异步处理放在持久层中,必须配合asyncpgsa或我们目前处理的数据库等第三方适配器使用。

from databases import Database
from sqlalchemy import create_engine
DATABASE_URL = "postgresql://user:password@server_addr/database
database = Database(DATABASE_URL)
engine = create_engine(DATABASE_URL)
view rawdatabase.py hosted with ❤ by GitHub
将要连接的数据库的 URI 放入 Databases 适配器,并将 URI 放入 SQLAlchemy 的 create_engine。

将 URI 放在这两个中的原因是负责数据库适配器中的实际事务并使用 SQLAlchemy 的核心创建查询。因此,这种异步处理工作如下。

SQLAlchemy 通过连接到数据库服务器所做的是定义表元数据。这里的元数据是指使用表的DDL定义,以后通过将定义的元数据发送到DB服务器,起到创建表的作用。

在这里,如果您使用 QueryDSL 方法的 SQLAlchemy Core 查询 DB 查询,并将此查询放入 Python DB API 函数(例如 fetch_all 或在 Databases 模块中执行),则事务工作并且应用程序接受它。

from sqlalchemy import Boolean, Column, Table, Metadata, String, Text
#…
metadata = Metadata()
memos = Table(
“memos”,
metadata,
Column(“id”, String(120), primary_key=True),
Column(“title”, String(80), default=‘No title’, nullable=False, index=True),
Column(“content”, Text, nullable=True)
Column(“is_favorite”, Boolean, nullable=False)
)
async def get_todos():
await database.connect()

SQLAlchemy core

query = memos.select()
results = await database.fetch_all(query)
await database.disconnect()
return results
view rawasync_database.pyhosted with ❤ by GitHub
之所以要走这么复杂的过程,是因为SQLAlchemy本身不支持asyncpg或aiopg等异步事务模块,需要单独使用一个支持它们的模块来使用。如果你想使用它,即使没有SQLAlchemy Core ,您可以仅使用 Databases 模块执行异步处理。

(新) SQLAlchemy 1.4.x + asyncpg
3月29日起,SQLAlchemy 1.4.x版本正式发布,SQLAlchemy中现已提供asyncpg。因此,只要有 SQLAlchemy 和连接池,就可以进行异步处理,而无需像上述方法那样使用单独的适配器进行异步处理。

因为SQLAlchemy支持asyncpg,所以在应用中不需要在asyncpg中声明函数或对象,直接使用SQLALchemy的create_async_engine函数来创建连接,并且Alchemy也提供了DB处理函数,可以继续进行。

from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
from sqlalchemy.orm import declarative_base
from typing import Optional
Base = declarative_base()
engine = create_async_engine(‘postgresql+asyncpg://user:pass@server_addr/database’, echo=True, pool_pre_ping=True)
async def get_db_session() -> AsyncSession:
sess = AsyncSession(bind=engine)
try:
yield sess
finally:
await sess.close()
view rawasync_alchemy.pyhosted with ❤ by GitHub
与现有的不同之处在于使用create_async_engine代替create_engine,如果你使用的是PostgreSQL,在postgresql后面附加+ asyncpg来区分连接类型。最后,Session还有一个单独的AsyncSession,可以看出在使用ORM处理DB时进一步加强了异步处理。

from sqlalchemy import Boolean, Column, String, Text
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
class Memo(Base):
tablename = ‘memos’
id = Column(String(120), primary_key=True, default=lambda: str(uuid.uuid4()))
title = Column(String(80), default=‘No title’, nullable=False, index=True)
content = Column(Text, nullable=True)
is_favorite = Column(Boolean, nullable=False, default=False)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
view rawasync_orm.py hosted with ❤ by GitHub
DDL处理ORM定义的一个类时,很明显应该进行同步处理。如果使用常用的create_all 函数,则与asyncio 不兼容,因为它是同步函数。

from fastapi import FastAPI, Depends
from sqlalchemy.asyncio import select
from sqlalchemy.orm import Session
from typing import Optional, List
app = FastAPI()
class ResponseMemo(BaseModel):
id: str
title: str
content: Optional[str] = None
is_favorite: bool
class Config:
orm_mode = True
@app.get(’/memos’, response_model=List[ResponseMemo])
async def get_memos(db: AsyncSession = Depends(get_db_session)):
stmt = select(Memo)
memos = await db.execute(stmt)
return memos.scalars().fetchall()
view rawasync_response.pyhosted with ❤ by GitHub
从 DB 中检索数据的方法也略有不同。之前,有一种使用位于sqlalchemy上方的select函数查询查询的方法,或者一种在ORM会话中使用查询函数查询查询的方法。但是,此方法不能用于以同步方式异步处理所有这些。

使用SQLAlchemy 1.4版本新增的sqlalchemy.ext.asyncio.select函数创建select查询,当使用它创建查询时,使用AsyncSession的execute异步方法获取查询执行结果,异步处理。

在此之后使用标量的情况下,它是一个遍历模型元数据并仅获取纯数据的函数,如果通过 fetchall 获取,则可以按原样重构现有代码。

from pydantic import BaseModel
from typing import Optional
class RequestMemo(BaseModel):
title: str
content: Optional[str] = None
is_favorite: Optional[bool] = False
class ResponseMemo(BaseModel):
id: str
title: str
content: Optional[str] = None
is_favorite: bool
class Config:
orm_mode = True
@app.post(’/memos’, response_model=ResponseMemo)
async def register_memo(req: RequestMemo, db: AsyncSession = Depends(get_db_session)):
memo = Memo(**req.dict())
db.add(memo)

이 코드를 쓰지 않으면 DB에 반영되지 않음

db.commit()
db.refresh(memo)
return memo
@app.put(’/memos/{item_id}’, response_model=ResponseMemo)
async def mod_memo(item_id: str, req: RequestMemo, db: AsyncSession = Depends(get_db_session)):
memo = db.query(Memo).filter_by(id=item_id)
req_dict = req.dict()
req_dict[‘id’] = item_id
req = {k: v for k, v in req_dict.items()}
for key, value in req.items():
setattr(memo, key, value)
db.commit()
db.refresh(memo)
return memo
view rawasync_transaction.pyhosted with ❤ by GitHub
如果你在SQLAlchemy中使用过select和where等非ORM函数,你可能尝试过session.refresh函数,但你可能不知道,因为刷新函数在ORM函数中很少使用。

在 SQLAlchemy 1.4 中,当创建或更新新数据时,您必须使用 refresh 方法来更新模型结果。因为 AsyncSession 基本上是一个基于 Session 的 AsyncEngine,它实际上和 Session 对象的工作原理几乎相同。

但是在AsyncSession中重新实现了commit等事务函数,通过greenlet_spawn函数进行异步处理,而没有重新实现add、expire等函数。

看代码可以看到Session类的方法使用了代理模式,为此,在更新add函数或模型时,使用了同步类。

结束…
除了 asyncpg 之外,还有另一个名为 aiopg 的模块用于 DB 部分的异步处理。了解使用哪个模块及其在您正在开发的项目中的性能应该是实现异步处理的第一步。

从SQLAlchemy 1.4版本开始,以后好像改成2.x的QueryDSL方式而不是JPQL方式了。因此,如果您打算继续使用 Python 作为后端开发,那么在将 ORM 用作 SQLAlchemy 时,最好记住这一点。

尽管本文以名为 asyncpg 的 PostgreSQL 作为主题进行了处理,但 Python DB API 中支持异步的模块如下。

MySQL (aiomysql)
PostgreSQL (aiopg, asyncpg)
SQLite (aiosqlite)
虽然有以上三个模块强力支持一种方言,但其他方言如MSSQL也可以使用aioodbc加载ODBC驱动然后使用。
喜欢3
分享
文本元素
订阅
归因相同的条件
“编程 > Python ”类别中的其他文章
[FastAPI] 11. 使用依赖注入器进行依赖管理 (0)2021.09.26
[FastAPI] 10.使用中间件进行后处理 (0)2021.05.02
[FastAPI] 9.如何异步处理持久层部分 (0)2021.04.04
[FastAPI] 8. 为什么SQLAlchemy的scoped_session是异步处理的问题 (0)2021.03.14
[FastAPI] 7.使用Google-auth和PyJWT 2实现OAuth2认证 (0)2021.02.27
[FastAPI] 6. 使用 Google-auth 和 PyJWT 实现 OAuth2 身份验证 1 (0)2021.02.13
标签。asyncpg , FastAPI , PostgreSQL ,SQLAlchemy
秘密
以前的

1

2

3

4

5

6

7
···

18

下一个
你好。这是霓虹小子。
类别
所有类别(235)
建筑学(7)
编程(109)
云平台(4)
嵌入式平台(26)
数据分析(6)
多发性硬化症(28)
开发运营(6)
基础设施(44)
热门文章
[Spring boot] 春天的嘘声⋯
注释
[MSA] 6. MSA的交易⋯
注释
[Python] 使用 REST API 开发⋯
注释
[FastAPI] 2. SQLAlchemy ⋯
注释
标签云
应用程序
Oauth2
弹簧靴
视窗 8
快速API
REST API
码头工人
日本特许经营协会
安卓
麻省理工学院
最新评论
谢谢你!!!
Jeong Jeong-jeong / 08.15
参考博客开发⋯
你好 / 07.12
重定向 URI 是 spring 代码⋯
霓虹小孩 / 07.10
你好。setRoleIfNo⋯
霓虹小孩 / 07.10
关联
NK 开发实验室
浪漫生活,浪漫发展
网络中的一切,都在……
STEVEN J. LEE - IT 技术博客
CCTT的“所有电缆”
DJ项目
建筑师之路
乌兹的
按回车
Seongju 的杂项技术博客
神吧乔阿
一号技术
♥Duck Duck’s Network Sarangbang♥
你好简妮
RastaLion.me
上山博客
点的数据库空间
桑树工作的 IT 聊天
铲狗
今天 39 / 昨天 123 / 全部 686,261 /管理员/ RSS
© WNSKIN。版权所有。/ 由 tistory 提供支持。
https://blog.neonkid.xyz/269?category=656103

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值