ปลดล็อกแอปพลิเคชันบนเว็บประสิทธิภาพสูงด้วยการเรียนรู้การรวมฐานข้อมูลแบบ asynchronous ใน FastAPI คู่มือฉบับสมบูรณ์พร้อมตัวอย่างไลบรารี SQLAlchemy และ Databases
การรวมฐานข้อมูล FastAPI: เจาะลึกการทำงานของฐานข้อมูลแบบ Asynchronous
ในโลกของการพัฒนาเว็บสมัยใหม่ ประสิทธิภาพไม่ใช่แค่คุณสมบัติ แต่เป็นข้อกำหนดพื้นฐาน ผู้ใช้คาดหวังแอปพลิเคชันที่รวดเร็วและตอบสนองได้ดี และนักพัฒนาก็แสวงหาเครื่องมือและเทคนิคอย่างต่อเนื่องเพื่อตอบสนองความคาดหวังเหล่านี้ FastAPI ได้กลายเป็นขุมพลังในระบบนิเวศของ Python ซึ่งเป็นที่ยอมรับในด้านความเร็วที่น่าทึ่ง ซึ่งส่วนใหญ่เป็นเพราะลักษณะการทำงานแบบ asynchronous อย่างไรก็ตาม เฟรมเวิร์กที่รวดเร็วเป็นเพียงส่วนหนึ่งของสมการเท่านั้น หากแอปพลิเคชันของคุณใช้เวลาส่วนใหญ่อยู่กับการรอฐานข้อมูลที่ช้า คุณได้สร้างเครื่องยนต์ประสิทธิภาพสูงที่ติดอยู่ในปัญหาการจราจร
นี่คือที่ที่การทำงานของฐานข้อมูลแบบ asynchronous กลายเป็นสิ่งสำคัญ ด้วยการอนุญาตให้แอปพลิเคชัน FastAPI ของคุณจัดการแบบสอบถามฐานข้อมูลโดยไม่กีดขวางกระบวนการทั้งหมด คุณสามารถปลดล็อกการทำงานพร้อมกันที่แท้จริงและสร้างแอปพลิเคชันที่ไม่เพียงแต่รวดเร็วเท่านั้น แต่ยังปรับขนาดได้สูงอีกด้วย คู่มือฉบับสมบูรณ์นี้จะแนะนำคุณเกี่ยวกับเหตุผล สิ่งที่ควรทำ และวิธีการรวมฐานข้อมูลแบบ asynchronous เข้ากับ FastAPI เพื่อให้คุณสามารถสร้างบริการที่มีประสิทธิภาพสูงอย่างแท้จริงสำหรับผู้ชมทั่วโลก
แนวคิดหลัก: ทำไม Asynchronous I/O ถึงสำคัญ
ก่อนที่เราจะเจาะลึกโค้ด สิ่งสำคัญคือต้องเข้าใจปัญหาพื้นฐานที่การทำงานแบบ async แก้ไขได้: การรอ I/O-bound
ลองนึกภาพพ่อครัวที่มีทักษะสูงในครัว ในรูปแบบ synchronous (หรือ blocking) พ่อครัวคนนี้จะทำงานทีละอย่าง พวกเขาจะใส่น้ำลงในหม้อบนเตาเพื่อต้มแล้วยืนดูจนกว่าจะเดือด หลังจากที่น้ำเดือดแล้วเท่านั้น พวกเขาจึงจะไปหั่นผัก นี่เป็นเรื่องที่ไม่มีประสิทธิภาพอย่างเหลือเชื่อ เวลาของพ่อครัว (CPU) จะสูญเสียไปในระหว่างช่วงเวลาที่รอ (การทำงานของ I/O)
ตอนนี้ ลองพิจารณาแบบจำลอง asynchronous (non-blocking) พ่อครัวใส่น้ำลงไปต้ม และแทนที่จะรอ ก็เริ่มหั่นผักทันที พวกเขาอาจใส่ถาดในเตาอบด้วย พวกเขาสามารถสลับไปมาระหว่างงานต่างๆ ทำให้เกิดความก้าวหน้าในหลายด้านในขณะที่รอการดำเนินการที่ช้ากว่า (เช่น การต้มน้ำหรือการอบ) เมื่อภารกิจเสร็จสิ้น (น้ำเดือด) พ่อครัวจะได้รับแจ้งและสามารถดำเนินการตามขั้นตอนต่อไปสำหรับจานนั้นได้
ในเว็บแอปพลิเคชัน แบบสอบถามฐานข้อมูล การเรียก API และการอ่านไฟล์เทียบเท่ากับการรอให้น้ำเดือด แอปพลิเคชัน synchronous แบบดั้งเดิมจะจัดการคำขอหนึ่งรายการ ส่งแบบสอบถามไปยังฐานข้อมูล จากนั้นนั่งเฉยๆ กีดขวางคำขอขาเข้าอื่นๆ จนกว่าฐานข้อมูลจะตอบสนอง แอปพลิเคชันแบบ asynchronous ขับเคลื่อนโดย `asyncio` ของ Python และเฟรมเวิร์กอย่าง FastAPI สามารถจัดการการเชื่อมต่อพร้อมกันได้หลายพันรายการโดยการสลับระหว่างการเชื่อมต่อเหล่านั้นอย่างมีประสิทธิภาพเมื่อมีการรอ I/O
ข้อดีหลักของการทำงานของฐานข้อมูลแบบ Async:
- เพิ่มการทำงานพร้อมกัน: จัดการผู้ใช้พร้อมกันจำนวนมากขึ้นอย่างมีนัยสำคัญด้วยทรัพยากรฮาร์ดแวร์เดียวกัน
- ปรับปรุง Throughput: ประมวลผลคำขอได้มากขึ้นต่อวินาที เนื่องจากแอปพลิเคชันไม่ติดขัดในการรอฐานข้อมูล
- ประสบการณ์ผู้ใช้ที่ดีขึ้น: เวลาตอบสนองที่เร็วขึ้นนำไปสู่ประสบการณ์ที่ตอบสนองและน่าพึงพอใจสำหรับผู้ใช้ปลายทาง
- ประสิทธิภาพของทรัพยากร: การใช้ CPU และหน่วยความจำได้ดีขึ้น ซึ่งอาจนำไปสู่ต้นทุนโครงสร้างพื้นฐานที่ต่ำลง
การตั้งค่าสภาพแวดล้อมการพัฒนาแบบ Asynchronous ของคุณ
ในการเริ่มต้น คุณจะต้องมีส่วนประกอบสำคัญสองสามอย่าง เราจะใช้ PostgreSQL เป็นฐานข้อมูลของเราสำหรับตัวอย่างเหล่านี้ เนื่องจากมีการสนับสนุนไดรเวอร์แบบ asynchronous ที่ยอดเยี่ยม อย่างไรก็ตาม หลักการต่างๆ จะนำไปใช้กับฐานข้อมูลอื่นๆ เช่น MySQL และ SQLite ที่มีไดรเวอร์ async
1. Core Framework และ Server
ขั้นแรก ติดตั้ง FastAPI และเซิร์ฟเวอร์ ASGI เช่น Uvicorn
pip install fastapi uvicorn[standard]
2. การเลือก Async Database Toolkit ของคุณ
คุณต้องมีส่วนประกอบหลักสองอย่างในการพูดคุยกับฐานข้อมูลของคุณแบบ asynchronous:
- An Async Database Driver: นี่คือไลบรารีระดับต่ำที่สื่อสารกับฐานข้อมูลผ่านเครือข่ายโดยใช้โปรโตคอล async สำหรับ PostgreSQL
asyncpgเป็นมาตรฐานโดยพฤตินัยและเป็นที่รู้จักในด้านประสิทธิภาพที่น่าทึ่ง - An Async Query Builder หรือ ORM: สิ่งนี้มีวิธีในการเขียนแบบสอบถามของคุณในระดับที่สูงขึ้นและเป็นแบบ Pythonic มากขึ้น เราจะสำรวจสองตัวเลือกยอดนิยม:
databases: ตัวสร้างแบบสอบถาม async ที่เรียบง่ายและน้ำหนักเบาซึ่งมี API ที่สะอาดสำหรับการดำเนินการ SQL ดิบSQLAlchemy 2.0+: SQLAlchemy ORM ที่มีประสิทธิภาพและมีคุณสมบัติครบถ้วนรุ่นล่าสุดมีการสนับสนุนดั้งเดิมและเป็นคลาสแรกสำหรับ `asyncio` บ่อยครั้ง นี่เป็นตัวเลือกที่ต้องการสำหรับแอปพลิเคชันที่ซับซ้อน
3. การติดตั้ง
มาติดตั้งไลบรารีที่จำเป็นกันดีกว่า คุณสามารถเลือกหนึ่งในชุดเครื่องมือหรือติดตั้งทั้งสองอย่างเพื่อทดลอง
สำหรับ PostgreSQL ด้วย SQLAlchemy และ `databases`:
# Driver for PostgreSQL
pip install asyncpg
# For the SQLAlchemy 2.0+ approach
pip install sqlalchemy
# For the 'databases' library approach
pip install databases[postgresql]
เมื่อสภาพแวดล้อมของเราพร้อมแล้ว มาสำรวจวิธีรวมเครื่องมือเหล่านี้เข้ากับแอปพลิเคชัน FastAPI กัน
กลยุทธ์ที่ 1: ความเรียบง่ายด้วยไลบรารี `databases`
ไลบรารี `databases` เป็นจุดเริ่มต้นที่ดีเยี่ยม ได้รับการออกแบบมาให้เรียบง่ายและมีตัวห่อบางๆ เหนือไดรเวอร์ async ที่อยู่เบื้องหลัง ทำให้คุณมีพลังของ async raw SQL โดยไม่ต้องมีความซับซ้อนของ ORM เต็มรูปแบบ
ขั้นตอนที่ 1: การเชื่อมต่อฐานข้อมูลและการจัดการวงจรชีวิต
ในแอปพลิเคชันจริง คุณไม่ต้องการเชื่อมต่อและตัดการเชื่อมต่อจากฐานข้อมูลในทุกคำขอ นี่ไม่มีประสิทธิภาพ แทนที่จะเป็นเช่นนั้น เราจะสร้าง connection pool เมื่อแอปพลิเคชันเริ่มต้นและปิดอย่างสง่างามเมื่อปิดตัวลง ตัวจัดการกิจกรรมของ FastAPI (`@app.on_event("startup")` และ `@app.on_event("shutdown")`) นั้นสมบูรณ์แบบสำหรับสิ่งนี้
มาสร้างไฟล์ชื่อ main_databases.py:
import databases
import sqlalchemy
from fastapi import FastAPI
# --- Database Configuration ---
# Replace with your actual database URL
# Format for asyncpg: "postgresql+asyncpg://user:password@host/dbname"
DATABASE_URL = "postgresql+asyncpg://user:password@localhost/testdb"
database = databases.Database(DATABASE_URL)
# SQLAlchemy model metadata (for table creation)
metadata = sqlalchemy.MetaData()
# Define a sample table
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)),
)
# Create an engine for table creation (this part is synchronous)
# The 'databases' library doesn't handle schema creation
engine = sqlalchemy.create_engine(DATABASE_URL.replace("+asyncpg", ""))
metadata.create_all(engine)
# --- FastAPI Application ---
app = FastAPI(title="FastAPI with Databases Library")
@app.on_event("startup")
async def startup():
print("Connecting to database...")
await database.connect()
print("Database connection established.")
@app.on_event("shutdown")
async def shutdown():
print("Disconnecting from database...")
await database.disconnect()
print("Database connection closed.")
# --- API Endpoints ---
@app.get("/")
def read_root():
return {"message": "Welcome to the Async Database API!"}
ประเด็นสำคัญ:
- เรากำหนด
DATABASE_URLโดยใช้รูปแบบpostgresql+asyncpg - มีการสร้างอ็อบเจกต์
databaseทั่วโลก - ตัวจัดการกิจกรรม
startupจะเรียกawait database.connect()ซึ่งเริ่มต้น connection pool - ตัวจัดการกิจกรรม
shutdownจะเรียกawait database.disconnect()เพื่อปิดการเชื่อมต่อทั้งหมดอย่างเรียบร้อย
ขั้นตอนที่ 2: การนำ CRUD Endpoints แบบ Asynchronous ไปใช้
ตอนนี้ มาเพิ่ม endpoints เพื่อดำเนินการสร้าง อ่าน อัปเดต และลบ (CRUD) กัน เราจะใช้ Pydantic สำหรับการตรวจสอบความถูกต้องของข้อมูลและการทำให้เป็นอนุกรม
เพิ่มสิ่งต่อไปนี้ลงในไฟล์ main_databases.py ของคุณ:
from pydantic import BaseModel
from typing import List, Optional
# --- Pydantic Models for data validation ---
class NoteIn(BaseModel):
title: str
content: str
class Note(BaseModel):
id: int
title: str
content: str
# --- CRUD Endpoints ---
@app.post("/notes/", response_model=Note)
async def create_note(note: NoteIn):
"""Create a new note in the database."""
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():
"""Retrieve all notes from the database."""
query = notes.select()
return await database.fetch_all(query)
@app.get("/notes/{note_id}", response_model=Note)
async def read_note(note_id: int):
"""Retrieve a single note by its ID."""
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="Note not found")
return result
@app.put("/notes/{note_id}", response_model=Note)
async def update_note(note_id: int, note: NoteIn):
"""Update an existing note."""
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="Note not found")
return {**note.dict(), "id": note_id}
@app.delete("/notes/{note_id}")
async def delete_note(note_id: int):
"""Delete a note by its ID."""
query = notes.delete().where(notes.c.id == note_id)
result = await database.execute(query)
if result == 0:
raise HTTPException(status_code=404, detail="Note not found")
return {"message": "Note deleted successfully"}
การวิเคราะห์การเรียก Async:
await database.execute(query): ใช้สำหรับการดำเนินการที่ไม่ส่งคืนแถว เช่น INSERT, UPDATE และ DELETE ส่งคืนจำนวนแถวที่ได้รับผลกระทบหรือคีย์หลักของระเบียนใหม่await database.fetch_all(query): ใช้สำหรับแบบสอบถาม SELECT ที่คุณคาดหวังหลายแถว ส่งคืนรายการระเบียนawait database.fetch_one(query): ใช้สำหรับแบบสอบถาม SELECT ที่คุณคาดหวังมากที่สุดหนึ่งแถว ส่งคืนระเบียนเดียวหรือNone
โปรดทราบว่าการโต้ตอบกับฐานข้อมูลทุกครั้งจะมีคำนำหน้าว่า await นี่คือเวทมนตร์ที่อนุญาตให้ event loop สลับไปยังงานอื่นๆ ในขณะที่รอให้ฐานข้อมูลตอบสนอง ทำให้เกิดการทำงานพร้อมกันสูง
กลยุทธ์ที่ 2: Modern Powerhouse - SQLAlchemy 2.0+ Async ORM
ในขณะที่ไลบรารี databases นั้นยอดเยี่ยมในเรื่องความเรียบง่าย แอปพลิเคชันขนาดใหญ่จำนวนมากได้รับประโยชน์จาก Object-Relational Mapper (ORM) ที่มีคุณสมบัติครบถ้วน ORM ช่วยให้คุณทำงานกับระเบียนฐานข้อมูลในรูปแบบอ็อบเจกต์ Python ซึ่งสามารถปรับปรุงประสิทธิภาพการทำงานของนักพัฒนาและดูแลรักษาโค้ดได้อย่างมาก SQLAlchemy เป็น ORM ที่มีประสิทธิภาพที่สุดในโลก Python และเวอร์ชัน 2.0+ ของรุ่นนี้มีอินเทอร์เฟซ async ดั้งเดิมที่ทันสมัย
ขั้นตอนที่ 1: การตั้งค่า Async Engine และ Session
หัวใจสำคัญของฟังก์ชันการทำงานแบบ async ของ SQLAlchemy อยู่ที่ AsyncEngine และ AsyncSession การตั้งค่าจะแตกต่างจากรุ่น synchronous เล็กน้อย
เราจะจัดระเบียบโค้ดของเราเป็นไฟล์สองสามไฟล์เพื่อโครงสร้างที่ดีขึ้น: database.py, models.py, schemas.py และ 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"
# Create an async engine
engine = create_async_engine(DATABASE_URL, echo=True)
# Create a session factory
# expire_on_commit=False prevents attributes from being expired after commit
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):
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` ในคลาสการกำหนดค่าของโมเดล Pydantic เป็นส่วนสำคัญของเวทมนตร์ มันบอก Pydantic ให้อ่านข้อมูลไม่ใช่แค่จากพจนานุกรม แต่ยังมาจากแอตทริบิวต์โมเดล ORM อีกด้วย
ขั้นตอนที่ 2: การจัดการ Sessions ด้วย Dependency Injection
วิธีที่แนะนำในการจัดการ database sessions ใน FastAPI คือผ่าน Dependency Injection เราจะสร้าง dependency ที่มี database session สำหรับคำขอเดียวและทำให้แน่ใจว่าได้ปิดหลังจากนั้น แม้ว่าจะมีข้อผิดพลาดเกิดขึ้นก็ตาม
เพิ่มสิ่งนี้ลงใน main_sqlalchemy.py ของคุณ:
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()
# --- Dependency for getting a DB session ---
async def get_db() -> AsyncSession:
async with AsyncSessionLocal() as session:
try:
yield session
finally:
await session.close()
# --- Database Initialization (for creating tables) ---
@app.on_event("startup")
async def startup_event():
print("Initializing database schema...")
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("Database schema initialized.")
dependency get_db เป็นเสาหลักของรูปแบบนี้ สำหรับแต่ละคำขอไปยัง endpoint ที่ใช้ มันจะ:
- สร้าง
AsyncSessionใหม่ yieldsession ไปยังฟังก์ชัน endpoint- โค้ดภายในบล็อก
finallyทำให้แน่ใจว่า session ถูกปิด โดยส่งคืนการเชื่อมต่อไปยัง pool โดยไม่คำนึงถึงว่าคำขอประสบความสำเร็จหรือไม่
ขั้นตอนที่ 3: การนำ Async CRUD ไปใช้กับ SQLAlchemy ORM
ตอนนี้เราสามารถเขียน endpoints ของเราได้ พวกเขาจะดูสะอาดตาและเน้นวัตถุมากกว่าวิธีการ SQL ดิบ
เพิ่ม endpoints เหล่านี้ลงใน 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="Note not found")
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="Note not found")
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="Note not found")
await db.delete(db_note)
await db.commit()
return {"message": "Note deleted successfully"}
การวิเคราะห์รูปแบบ SQLAlchemy Async:
db: AsyncSession = Depends(get_db): สิ่งนี้จะแทรก database session ของเราลงใน endpointawait db.execute(...): นี่คือวิธีหลักในการเรียกใช้แบบสอบถามresult.scalars().all()/result.scalar_one_or_none(): วิธีการเหล่านี้ใช้เพื่อดึงอ็อบเจกต์ ORM จริงจากผลลัพธ์แบบสอบถามdb.add(obj): จัดเตรียมอ็อบเจกต์ที่จะแทรกawait db.commit(): คอมมิตธุรกรรมไปยังฐานข้อมูลแบบ asynchronous นี่คือจุด `await` ที่สำคัญawait db.refresh(obj): รีเฟรชอ็อบเจกต์ Python ด้วยข้อมูลใหม่จากฐานข้อมูลหลังจากการคอมมิต (เช่น ID ที่สร้างขึ้นอัตโนมัติ)
ข้อควรพิจารณาด้านประสิทธิภาพและแนวทางปฏิบัติที่ดีที่สุด
การใช้ `async` และ `await` นั้นเป็นจุดเริ่มต้นที่ดี แต่ในการสร้างแอปพลิเคชันที่มีความแข็งแกร่งและมีประสิทธิภาพสูงอย่างแท้จริง ให้พิจารณาแนวทางปฏิบัติที่ดีที่สุดเหล่านี้
1. ทำความเข้าใจ Connection Pooling
ทั้ง databases และ AsyncEngine ของ SQLAlchemy จัดการ connection pool ที่อยู่เบื้องหลัง connection pool นี้จะรักษาชุดของการเชื่อมต่อฐานข้อมูลแบบเปิดที่สามารถนำกลับมาใช้ใหม่ได้โดยคำขอที่แตกต่างกัน สิ่งนี้หลีกเลี่ยงค่าใช้จ่ายสูงในการสร้างการเชื่อมต่อ TCP ใหม่และตรวจสอบสิทธิ์กับฐานข้อมูลสำหรับทุกแบบสอบถาม คุณสามารถปรับขนาด pool (เช่น `pool_size`, `max_overflow`) ในการกำหนดค่า engine สำหรับเวิร์กโหลดเฉพาะของคุณ
2. ห้ามผสมผสานการเรียกฐานข้อมูลแบบ Sync และ Async
กฎที่สำคัญที่สุดคือ ห้ามเรียกฟังก์ชัน I/O แบบ synchronous ที่บล็อกภายในฟังก์ชัน `async def` การเรียกฐานข้อมูลแบบ synchronous มาตรฐาน (เช่น การใช้ `psycopg2` โดยตรง) จะบล็อก event loop ทั้งหมด ทำให้แอปพลิเคชันของคุณหยุดทำงานและทำลายจุดประสงค์ของ async
หากคุณต้องเรียกใช้โค้ดแบบ synchronous อย่างแน่นอน (อาจเป็นไลบรารีแบบ CPU-bound) ให้ใช้ `run_in_threadpool` ของ FastAPI เพื่อหลีกเลี่ยงการบล็อก event loop:
from fastapi.concurrency import run_in_threadpool
@app.get("/run-sync-task/")
async def run_sync_task():
# 'some_blocking_io_function' is a regular sync function
result = await run_in_threadpool(some_blocking_io_function, arg1, arg2)
return {"result": result}
3. ใช้ธุรกรรมแบบ Asynchronous
เมื่อการดำเนินการเกี่ยวข้องกับการเปลี่ยนแปลงฐานข้อมูลหลายรายการที่ต้องสำเร็จหรือล้มเหลวร่วมกัน (การดำเนินการแบบอะตอม) คุณต้องใช้ธุรกรรม ทั้งไลบรารีรองรับสิ่งนี้ผ่าน async context manager
ด้วย `databases`:
async def transfer_funds():
async with database.transaction():
await database.execute(query_for_debit)
await database.execute(query_for_credit)
ด้วย SQLAlchemy:
async def transfer_funds(db: AsyncSession = Depends(get_db)):
async with db.begin(): # This starts a transaction
# Find accounts
account_from = ...
account_to = ...
# Update balances
account_from.balance -= 100
account_to.balance += 100
# The transaction is automatically committed on exiting the block
# or rolled back if an exception occurs.
4. เลือกเฉพาะสิ่งที่คุณต้องการ
หลีกเลี่ยง `SELECT *` เมื่อคุณต้องการเพียงไม่กี่คอลัมน์ การถ่ายโอนข้อมูลน้อยลงผ่านเครือข่ายจะช่วยลดเวลาการรอ I/O ด้วย SQLAlchemy คุณสามารถใช้ `options(load_only(model.col1, model.col2))` เพื่อระบุคอลัมน์ที่จะดึงข้อมูล
บทสรุป: โอบรับอนาคตแบบ Asynchronous
การรวมการดำเนินการของฐานข้อมูลแบบ asynchronous เข้ากับแอปพลิเคชัน FastAPI ของคุณเป็นกุญแจสำคัญในการปลดล็อกศักยภาพด้านประสิทธิภาพอย่างเต็มที่ ด้วยการรับรองว่าแอปพลิเคชันของคุณจะไม่บล็อกขณะรอฐานข้อมูล คุณสามารถสร้างบริการที่รวดเร็ว ปรับขนาดได้ และมีประสิทธิภาพอย่างเหลือเชื่อ ซึ่งสามารถให้บริการผู้ใช้ทั่วโลกได้โดยไม่ยาก
เราได้สำรวจกลยุทธ์สองแบบที่มีประสิทธิภาพ:
- ไลบรารี `databases` มีแนวทางที่ตรงไปตรงมาและน้ำหนักเบาสำหรับนักพัฒนาที่ต้องการเขียน SQL และต้องการอินเทอร์เฟซ async ที่เรียบง่ายและรวดเร็ว
- SQLAlchemy 2.0+ มี ORM ที่มีคุณสมบัติครบถ้วนและแข็งแกร่งพร้อม API async ดั้งเดิม ทำให้เป็นตัวเลือกที่เหมาะสมที่สุดสำหรับแอปพลิเคชันที่ซับซ้อนซึ่งความสามารถในการผลิตและความสามารถในการบำรุงรักษาของนักพัฒนาเป็นสิ่งสำคัญ
ตัวเลือกระหว่างทั้งสองขึ้นอยู่กับความต้องการของโปรเจกต์ของคุณ แต่หลักการสำคัญยังคงเหมือนเดิม: คิดแบบ non-blocking ด้วยการนำรูปแบบและแนวทางปฏิบัติที่ดีที่สุดเหล่านี้ไปใช้ คุณไม่ได้เป็นเพียงการเขียนโค้ด คุณกำลังออกแบบระบบสำหรับความต้องการพร้อมกันสูงของเว็บสมัยใหม่ เริ่มสร้างแอปพลิเคชัน FastAPI ประสิทธิภาพสูงถัดไปของคุณได้แล้ววันนี้ และสัมผัสกับพลังของ Python แบบ asynchronous โดยตรง