Avage kõrgjõudlusega veebirakenduste potentsiaal, õppides selgeks asünkroonse andmebaasi integreerimise FastAPI-s. Põhjalik juhend koos SQLAlchemy ja Databases teekide näidetega.
FastAPI andmebaasi integreerimine: süvauuring asünkroonsetest andmebaasioperatsioonidest
Kaasaegses veebiarenduse maailmas pole jõudlus lihtsalt omadus, vaid fundamentaalne nõue. Kasutajad ootavad kiireid ja reageerivaid rakendusi ning arendajad otsivad pidevalt vahendeid ja tehnikaid nende ootuste täitmiseks. FastAPI on tõusnud Pythoni ökosüsteemis esile kui jõujaam, mida kiidetakse selle uskumatu kiiruse eest, mis on suuresti tingitud selle asünkroonsest olemusest. Kuid kiire raamistik on vaid üks osa võrrandist. Kui teie rakendus veedab suurema osa ajast aeglase andmebaasi ootel, olete loonud suure jõudlusega mootori, mis on ummikus.
See on koht, kus asünkroonsed andmebaasioperatsioonid muutuvad kriitiliseks. Lubades oma FastAPI rakendusel käsitleda andmebaasipäringuid ilma kogu protsessi blokeerimata, saate avada tõelise samaaegsuse ja ehitada rakendusi, mis pole mitte ainult kiired, vaid ka väga skaleeritavad. See põhjalik juhend juhatab teid läbi asünkroonsete andmebaaside integreerimise miks'i, mis'i ja kuidas'i FastAPI-ga, andes teile võimekuse ehitada tõeliselt suure jõudlusega teenuseid globaalsele publikule.
Põhikontseptsioon: miks asünkroonne I/O on oluline
Enne koodi sukeldumist on ülioluline mõista põhiprobleemi, mida asünkroonsed operatsioonid lahendavad: I/O-ga seotud ootamine.
Kujutage ette kõrgelt kvalifitseeritud kokka köögis. Sünkroonses (või blokeerivas) mudelis teostaks see kokk ühte ülesannet korraga. Ta paneks poti veega pliidile keema ja seisaks siis seal, vaadates seda, kuni see keeb. Alles pärast vee keemist liiguks ta edasi köögiviljade hakkimise juurde. See on uskumatult ebaefektiivne. Koka aeg (protsessor) raisatakse ooteperioodil (I/O operatsioon).
Nüüd kaaluge asünkroonset (mitteblokeerivat) mudelit. Kokk paneb vee keema ja selle asemel, et oodata, hakkab kohe köögivilju hakkima. Ta võib ka panna ahjuplaadi ahju. Ta saab vahetada ülesannete vahel, tehes edusamme mitmel rindel, oodates samal ajal aeglasemate operatsioonide (nagu vee keetmine või küpsetamine) lõpuleviimist. Kui ülesanne on lõpetatud (vesi keeb), teavitatakse kokka ja ta saab selle roa järgmise sammuga edasi minna.
Veebirakenduses on andmebaasipäringud, API-kõned ja failide lugemine samaväärsed vee keemise ootamisega. Traditsiooniline sünkroonne rakendus käsitleks ühte päringut, saadaks päringu andmebaasi ja istuks seejärel tegevusetult, blokeerides kõik muud sissetulevad päringud, kuni andmebaas vastab. Asünkroonne rakendus, mida toetavad Pythoni `asyncio` ja raamistikud nagu FastAPI, suudab käsitleda tuhandeid samaaegseid ühendusi, lülitudes nende vahel tõhusalt ümber, kui üks neist ootab I/O-d.
Asünkroonsete andmebaasioperatsioonide peamised eelised:
- Suurenenud samaaegsus: Käsitlege oluliselt suuremat arvu samaaegseid kasutajaid samade riistvararessurssidega.
- Parem läbilaskevõime: Töödelge rohkem päringuid sekundis, kuna rakendus ei jää andmebaasi ootama.
- Parem kasutajakogemus: Kiiremad vastuseajad tagavad lõppkasutajale reageerivama ja rahuldavama kogemuse.
- Ressursitõhusus: Parem protsessori ja mälu kasutus, mis võib kaasa tuua madalamad taristukulud.
Asünkroonse arenduskeskkonna seadistamine
Alustamiseks on teil vaja mõnda põhikomponenti. Nendes näidetes kasutame andmebaasina PostgreSQL-i, kuna sellel on suurepärane tugi asünkroonsetele draiveritele. Kuid põhimõtted kehtivad ka teistele andmebaasidele nagu MySQL ja SQLite, millel on asünkroonsed draiverid.
1. Tuumikraamistik ja server
Esmalt installige FastAPI ja ASGI server nagu Uvicorn.
pip install fastapi uvicorn[standard]
2. Oma asünkroonse andmebaasi tööriistakomplekti valimine
Teil on vaja kahte põhikomponenti, et oma andmebaasiga asünkroonselt suhelda:
- Asünkroonne andmebaasidraiver: See on madala taseme teek, mis suhtleb andmebaasiga üle võrgu asünkroonse protokolli abil. PostgreSQL-i jaoks on
asyncpgde facto standard ja tuntud oma uskumatu jõudluse poolest. - Asünkroonne päringuehitaja või ORM: See pakub kõrgema taseme, pythonilikumat viisi oma päringute kirjutamiseks. Uurime kahte populaarset valikut:
databases: lihtne ja kerge asünkroonne päringuehitaja, mis pakub puhast API-d toore SQL-i käivitamiseks.SQLAlchemy 2.0+: võimsa ja funktsioonirikka SQLAlchemy ORM-i uusimad versioonid sisaldavad natiivset, esmaklassilist tuge `asyncio` jaoks. See on sageli eelistatud valik keerukate rakenduste jaoks.
3. Installimine
Installime vajalikud teegid. Võite valida ühe tööriistakomplekti või installida mõlemad, et katsetada.
PostgreSQL-i jaoks koos SQLAlchemy ja `databases`'iga:
# Draiver PostgreSQL-i jaoks
pip install asyncpg
# SQLAlchemy 2.0+ lähenemise jaoks
pip install sqlalchemy
# 'databases' teegi lähenemise jaoks
pip install databases[postgresql]
Kui meie keskkond on valmis, uurime, kuidas neid tööriistu FastAPI rakendusse integreerida.
Strateegia 1: Lihtsus `databases` teegiga
databases teek on suurepärane lähtepunkt. See on loodud olema lihtne ja pakub õhukest kihti aluseks olevate asünkroonsete draiverite peal, andes teile asünkroonse toore SQL-i võimsuse ilma täisfunktsionaalse ORM-i keerukuseta.
Samm 1: Andmebaasiühendus ja elutsükli haldamine
Päris rakenduses ei taha te igal päringul andmebaasiga ühendust luua ja katkestada. See on ebaefektiivne. Selle asemel loome rakenduse käivitamisel ühenduste kogumi (connection pool) ja sulgeme selle graatsiliselt, kui rakendus seiskub. FastAPI sündmuste käsitlejad (`@app.on_event("startup")` ja `@app.on_event("shutdown")`) sobivad selleks ideaalselt.
Loome faili nimega main_databases.py:
import databases
import sqlalchemy
from fastapi import FastAPI
# --- Andmebaasi konfiguratsioon ---
# Asendage oma tegeliku andmebaasi URL-iga
# asyncpg vorming: "postgresql+asyncpg://kasutaja:parool@host/andmebaasinimi"
DATABASE_URL = "postgresql+asyncpg://user:password@localhost/testdb"
database = databases.Database(DATABASE_URL)
# SQLAlchemy mudeli metaandmed (tabeli loomiseks)
metadata = sqlalchemy.MetaData()
# Määratlege näidistabel
notes = sqlalchemy.Table(
"notes",
metadata,
sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
sqlalchemy.Column("title", sqlalchemy.String(100)),
sqlalchemy.Column("content", sqlalchemy.String(500)),
)
# Looge mootor tabeli loomiseks (see osa on sünkroonne)
# 'databases' teek ei tegele skeemi loomisega
engine = sqlalchemy.create_engine(DATABASE_URL.replace("+asyncpg", ""))
metadata.create_all(engine)
# --- FastAPI rakendus ---
app = FastAPI(title="FastAPI with Databases Library")
@app.on_event("startup")
async def startup():
print("Ühendun andmebaasiga...")
await database.connect()
print("Andmebaasiühendus loodud.")
@app.on_event("shutdown")
async def shutdown():
print("Katkestan ühenduse andmebaasiga...")
await database.disconnect()
print("Andmebaasiühendus suletud.")
# --- API otspunktid ---
@app.get("/")
def read_root():
return {"message": "Tere tulemast Async Database API-sse!"}
Põhipunktid:
- Määratleme
DATABASE_URL-i, kasutadespostgresql+asyncpgskeemi. - Luukase globaalne
databaseobjekt. startupsündmuse käsitleja kutsub väljaawait database.connect(), mis initsialiseerib ühenduste kogumi.shutdownsündmuse käsitleja kutsub väljaawait database.disconnect(), et kõik ühendused puhtalt sulgeda.
Samm 2: Asünkroonsete CRUD otspunktide implementeerimine
Nüüd lisame otspunktid loomise, lugemise, uuendamise ja kustutamise (CRUD) operatsioonide teostamiseks. Kasutame ka Pydanticut andmete valideerimiseks ja serialiseerimiseks.
Lisage järgnev oma main_databases.py faili:
from pydantic import BaseModel
from typing import List, Optional
# --- Pydantic mudelid andmete valideerimiseks ---
class NoteIn(BaseModel):
title: str
content: str
class Note(BaseModel):
id: int
title: str
content: str
# --- CRUD otspunktid ---
@app.post("/notes/", response_model=Note)
async def create_note(note: NoteIn):
"""Looge andmebaasi uus märge."""
query = notes.insert().values(title=note.title, content=note.content)
last_record_id = await database.execute(query)
return {**note.dict(), "id": last_record_id}
@app.get("/notes/", response_model=List[Note])
async def read_all_notes():
"""Hankige kõik märkmed andmebaasist."""
query = notes.select()
return await database.fetch_all(query)
@app.get("/notes/{note_id}", response_model=Note)
async def read_note(note_id: int):
"""Hankige üksik märge selle ID järgi."""
query = notes.select().where(notes.c.id == note_id)
result = await database.fetch_one(query)
if result is None:
raise HTTPException(status_code=404, detail="Märget ei leitud")
return result
@app.put("/notes/{note_id}", response_model=Note)
async def update_note(note_id: int, note: NoteIn):
"""Uuendage olemasolevat märget."""
query = (
notes.update()
.where(notes.c.id == note_id)
.values(title=note.title, content=note.content)
)
result = await database.execute(query)
if result == 0:
raise HTTPException(status_code=404, detail="Märget ei leitud")
return {**note.dict(), "id": note_id}
@app.delete("/notes/{note_id}")
async def delete_note(note_id: int):
"""Kustutage märge selle ID järgi."""
query = notes.delete().where(notes.c.id == note_id)
result = await database.execute(query)
if result == 0:
raise HTTPException(status_code=404, detail="Märget ei leitud")
return {"message": "Märge edukalt kustutatud"}
Asünkroonsete kutsete analüüs:
await database.execute(query): Kasutatakse operatsioonide jaoks, mis ei tagasta ridu, nagu INSERT, UPDATE ja DELETE. See tagastab mõjutatud ridade arvu või uue kirje primaarvõtme.await database.fetch_all(query): Kasutatakse SELECT päringute jaoks, kus ootate mitut rida. See tagastab kirjete loendi.await database.fetch_one(query): Kasutatakse SELECT päringute jaoks, kus ootate maksimaalselt ühte rida. See tagastab ühe kirje võiNone.
Pange tähele, et iga andmebaasi interaktsiooni ees on await. See on maagia, mis võimaldab sündmuste ahelal (event loop) lülituda teistele ülesannetele, oodates samal ajal andmebaasi vastust, võimaldades suurt samaaegsust.
Strateegia 2: Kaasaegne jõujaam - SQLAlchemy 2.0+ asünkroonne ORM
Kuigi databases teek on suurepärane lihtsuse poolest, saavad paljud suuremahulised rakendused kasu täisfunktsionaalsest objekt-relatsioonilisest vastendajast (ORM). ORM võimaldab teil töötada andmebaasikirjetega kui Pythoni objektidega, mis võib oluliselt parandada arendaja tootlikkust ja koodi hooldatavust. SQLAlchemy on Pythoni maailma võimsaim ORM ja selle 2.0+ versioonid pakuvad tipptasemel natiivset asünkroonset liidest.
Samm 1: Asünkroonse mootori ja seansi seadistamine
SQLAlchemy asünkroonse funktsionaalsuse tuum peitub AsyncEngine'is ja AsyncSession'is. Seadistus on sünkroonsest versioonist veidi erinev.
Parema struktuuri huvides korraldame oma koodi mõnda faili: database.py, models.py, schemas.py ja main_sqlalchemy.py.
database.py:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "postgresql+asyncpg://user:password@localhost/testdb"
# Loo asünkroonne mootor
engine = create_async_engine(DATABASE_URL, echo=True)
# Loo seansivabrik (session factory)
# expire_on_commit=False takistab atribuutide aegumist pärast commit'i
AsyncSessionLocal = sessionmaker(
bind=engine, class_=AsyncSession, expire_on_commit=False
)
models.py:
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class Note(Base):
__tablename__ = "notes"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(100), index=True)
content = Column(String(500))
schemas.py (Pydantic mudelid):
from pydantic import BaseModel
class NoteBase(BaseModel):
title: str
content: str
class NoteCreate(NoteBase):
pass
class Note(NoteBase):
id: int
class Config:
orm_mode = True
`orm_mode = True` Pydanticu mudeli konfiguratsiooniklassis on oluline maagiline osa. See ütleb Pydanticule, et see loeks andmeid mitte ainult sõnastikest, vaid ka ORM-i mudeli atribuutidest.
Samm 2: Seansside haldamine sõltuvuste süstimisega (Dependency Injection)
Soovitatav viis andmebaasiseansside haldamiseks FastAPI-s on sõltuvuste süstimise kaudu. Loome sõltuvuse, mis pakub andmebaasiseanssi ühe päringu jaoks ja tagab selle hilisema sulgemise, isegi kui tekib viga.
Lisage see oma main_sqlalchemy.py faili:
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from . import models, schemas
from .database import engine, AsyncSessionLocal
app = FastAPI()
# --- Sõltuvus andmebaasiseansi saamiseks ---
async def get_db() -> AsyncSession:
async with AsyncSessionLocal() as session:
try:
yield session
finally:
await session.close()
# --- Andmebaasi initsialiseerimine (tabelite loomiseks) ---
@app.on_event("startup")
async def startup_event():
print("Initsialiseerin andmebaasi skeemi...")
async with engine.begin() as conn:
# await conn.run_sync(models.Base.metadata.drop_all)
await conn.run_sync(models.Base.metadata.create_all)
print("Andmebaasi skeem initsialiseeritud.")
get_db sõltuvus on selle mustri nurgakivi. Iga päringu puhul, mis seda kasutab, see:
- Loob uue
AsyncSession'i. yield'ib seansi otspunkti funktsioonile.- Kood
finallyplokis tagab seansi sulgemise, tagastades ühenduse kogumisse, olenemata sellest, kas päring oli edukas või mitte.
Samm 3: Asünkroonse CRUD-i implementeerimine SQLAlchemy ORM-iga
Nüüd saame kirjutada oma otspunktid. Need näevad välja puhtamad ja objektorienteeritumad kui toore SQL-i lähenemine.
Lisage need otspunktid faili main_sqlalchemy.py:
@app.post("/notes/", response_model=schemas.Note)
async def create_note(
note: schemas.NoteCreate, db: AsyncSession = Depends(get_db)
):
db_note = models.Note(title=note.title, content=note.content)
db.add(db_note)
await db.commit()
await db.refresh(db_note)
return db_note
@app.get("/notes/", response_model=list[schemas.Note])
async def read_all_notes(skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(models.Note).offset(skip).limit(limit))
notes = result.scalars().all()
return notes
@app.get("/notes/{note_id}", response_model=schemas.Note)
async def read_note(note_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(models.Note).filter(models.Note.id == note_id))
db_note = result.scalar_one_or_none()
if db_note is None:
raise HTTPException(status_code=404, detail="Märget ei leitud")
return db_note
@app.put("/notes/{note_id}", response_model=schemas.Note)
async def update_note(
note_id: int, note: schemas.NoteCreate, db: AsyncSession = Depends(get_db)
):
result = await db.execute(select(models.Note).filter(models.Note.id == note_id))
db_note = result.scalar_one_or_none()
if db_note is None:
raise HTTPException(status_code=404, detail="Märget ei leitud")
db_note.title = note.title
db_note.content = note.content
await db.commit()
await db.refresh(db_note)
return db_note
@app.delete("/notes/{note_id}")
async def delete_note(note_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(models.Note).filter(models.Note.id == note_id))
db_note = result.scalar_one_or_none()
if db_note is None:
raise HTTPException(status_code=404, detail="Märget ei leitud")
await db.delete(db_note)
await db.commit()
return {"message": "Märge edukalt kustutatud"}
SQLAlchemy asünkroonse mustri analüüs:
db: AsyncSession = Depends(get_db): See süstib meie andmebaasiseansi otspunkti.await db.execute(...): See on peamine meetod päringute käivitamiseks.result.scalars().all()/result.scalar_one_or_none(): Neid meetodeid kasutatakse tegelike ORM-objektide eraldamiseks päringu tulemusest.db.add(obj): Seab objekti sisestamiseks valmis.await db.commit(): Kinnitab asünkroonselt tehingu andmebaasi. See on ülioluline `await` punkt.await db.refresh(obj): Värskendab Pythoni objekti mis tahes uute andmetega andmebaasist pärast kinnitamist (nagu automaatselt genereeritud ID).
Jõudlusega seotud kaalutlused ja parimad praktikad
Lihtsalt `async` ja `await` kasutamine on suurepärane algus, kuid tõeliselt robustsete ja suure jõudlusega rakenduste ehitamiseks kaaluge neid parimaid praktikaid.
1. Mõistke ühenduste kogumit (Connection Pooling)
Nii databases kui ka SQLAlchemy AsyncEngine haldavad taustal ühenduste kogumit. See kogum hoiab avatud andmebaasiühenduste komplekti, mida erinevad päringud saavad taaskasutada. See väldib kalli uue TCP-ühenduse loomise ja andmebaasiga autentimise kulu iga päringu jaoks. Saate häälestada kogumi suurust (nt `pool_size`, `max_overflow`) mootori konfiguratsioonis vastavalt oma konkreetsele töökoormusele.
2. Ärge kunagi segage sünkroonseid ja asünkroonseid andmebaasikutseid
Kõige olulisem reegel on mitte kunagi kutsuda sünkroonset, blokeerivat I/O funktsiooni `async def` funktsiooni sees. Standardne, sünkroonne andmebaasikutse (nt kasutades otse `psycopg2`) blokeerib kogu sündmuste ahela, külmutades teie rakenduse ja nullides asünkroonsuse eesmärgi.
Kui peate absoluutselt käivitama sünkroonse koodijupi (võib-olla protsessorimahuka teegi), kasutage FastAPI `run_in_threadpool` funktsiooni, et vältida sündmuste ahela blokeerimist:
from fastapi.concurrency import run_in_threadpool
@app.get("/run-sync-task/")
async def run_sync_task():
# 'some_blocking_io_function' on tavaline sünkroonne funktsioon
result = await run_in_threadpool(some_blocking_io_function, arg1, arg2)
return {"result": result}
3. Kasutage asünkroonseid tehinguid
Kui operatsioon hõlmab mitut andmebaasi muudatust, mis peavad koos õnnestuma või ebaõnnestuma (atomaarne operatsioon), peate kasutama tehingut. Mõlemad teegid toetavad seda asünkroonse kontekstihalduri kaudu.
databases'iga:
async def transfer_funds():
async with database.transaction():
await database.execute(query_for_debit)
await database.execute(query_for_credit)
SQLAlchemy'ga:
async def transfer_funds(db: AsyncSession = Depends(get_db)):
async with db.begin(): # See alustab tehingut
# Leia kontod
account_from = ...
account_to = ...
# Uuenda saldosid
account_from.balance -= 100
account_to.balance += 100
# Tehing kinnitatakse automaatselt plokist väljumisel
# või tühistatakse, kui tekib erand.
4. Valige ainult see, mida vajate
Vältige `SELECT *`, kui vajate ainult mõnda veergu. Väiksema andmehulga edastamine üle võrgu vähendab I/O ooteaega. SQLAlchemy'ga saate kasutada `options(load_only(model.col1, model.col2))`, et määrata, milliseid veerge hankida.
Kokkuvõte: Võtke omaks asünkroonne tulevik
Asünkroonsete andmebaasioperatsioonide integreerimine oma FastAPI rakendusse on võti selle täieliku jõudluspotentsiaali avamiseks. Tagades, et teie rakendus ei blokeeru andmebaasi oodates, saate ehitada teenuseid, mis on uskumatult kiired, skaleeritavad ja tõhusad, võimelised teenindama globaalset kasutajaskonda ilma higistamata.
Oleme uurinud kahte võimsat strateegiat:
- `databases` teek pakub otsekohest ja kerget lähenemist arendajatele, kes eelistavad kirjutada SQL-i ja vajavad lihtsat ning kiiret asünkroonset liidest.
- SQLAlchemy 2.0+ pakub täisfunktsionaalset ja robustset ORM-i koos natiivse asünkroonse API-ga, muutes selle ideaalseks valikuks keerukate rakenduste jaoks, kus arendaja tootlikkus ja hooldatavus on esmatähtsad.
Valik nende vahel sõltub teie projekti vajadustest, kuid põhiprintsiip jääb samaks: mõtle mitteblokeerivalt. Nende mustrite ja parimate praktikate omaksvõtmisega ei kirjuta te lihtsalt koodi; te arhitektuurite süsteeme kaasaegse veebi suure samaaegsuse nõudmistele. Alustage oma järgmise suure jõudlusega FastAPI rakenduse ehitamist juba täna ja kogege asünkroonse Pythoni jõudu omal nahal.