SQLAlchemy:异步、连接池 (四)

一、异步 SQLAlchemy

传统 SQLAlchemy 是同步的,在异步框架(如 FastAPI)中会阻塞事件循环。异步 SQLAlchemy 可以在 async 函数中直接 await 数据库操作。

安装

复制代码
# 异步驱动
pip install asyncpg          # PostgreSQL
pip install aiomysql         # MySQL
pip install aiosqlite        # SQLite

创建异步 Engine

复制代码
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine(
    "postgresql+asyncpg://user:pass@localhost:5432/mydb",
    echo=True,
    pool_size=10,
    max_overflow=20
)

连接字符串变化 :postgresql+psycopg2 → postgresql+asyncpg

创建异步 Session

复制代码
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

AsyncSessionLocal = async_sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False
)

异步 CRUD

复制代码
from sqlalchemy import select

async def get_users():
    async with AsyncSessionLocal() as session:
        result = await session.execute(select(User))
        return result.scalars().all()

async def create_user(name: str):
    async with AsyncSessionLocal() as session:
        user = User(name=name)
        session.add(user)
        await session.commit()
        await session.refresh(user)
        return user

区别:

  • session.execute() → await session.execute()

  • session.commit() → await session.commit()

  • session.refresh() → await session.refresh()

异步预加载

复制代码
from sqlalchemy.orm import selectinload

result = await session.execute(
    select(User).options(selectinload(User.orders))
)
users = result.scalars().all()

二、连接池配置

连接池管理数据库连接,避免每次请求都新建连接。

常用参数

复制代码
engine = create_engine(
    "mysql+pymysql://user:pass@localhost:3306/mydb",
    pool_size=10,          # 连接池大小
    max_overflow=20,       # 超出 pool_size 后最多创建的连接数
    pool_timeout=30,       # 获取连接的超时时间(秒)
    pool_recycle=3600,     # 连接回收时间(秒),避免 MySQL 8 小时断连
    pool_pre_ping=True,    # 使用前检查连接是否有效
    echo=False
)
参数 说明 建议值
pool_size 连接池大小 10-20
max_overflow 额外连接数 pool_size 的 2 倍
pool_timeout 获取连接超时 30
pool_recycle 连接回收时间 3600(小于 MySQL 的 8 小时)
pool_pre_ping 使用前 ping True

连接池监控

复制代码
# 查看连接池状态
print(engine.pool.status())
# 输出:Pool size: 10 Connections in pool: 2 Current Overflow: 0

三、与 FastAPI 集成

1. 项目结构

复制代码
myapp/
├── main.py
├── database.py       # 数据库配置
├── models.py         # 模型定义
├── schemas.py        # Pydantic 模型
├── crud.py           # 数据库操作
└── routers/
    └── users.py      # 路由

2. database.py

复制代码
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession

DATABASE_URL = "postgresql+asyncpg://user:pass@localhost:5432/mydb"

engine = create_async_engine(
    DATABASE_URL,
    pool_size=10,
    max_overflow=20,
    pool_pre_ping=True,
    echo=False
)

AsyncSessionLocal = async_sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False
)

async def get_db():
    """依赖注入:每个请求一个 Session"""
    async with AsyncSessionLocal() as session:
        try:
            yield session
        finally:
            await session.close()

3. models.py

复制代码
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.orm import declarative_base
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)
    email = Column(String(100), unique=True)
    created_at = Column(DateTime, default=datetime.now)

4. schemas.py

复制代码
from pydantic import BaseModel, EmailStr
from datetime import datetime

class UserCreate(BaseModel):
    name: str
    email: EmailStr

class UserResponse(BaseModel):
    id: int
    name: str
    email: str
    created_at: datetime
    
    class Config:
        from_attributes = True  # 支持从 ORM 对象转换

5. crud.py

复制代码
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from models import User
from schemas import UserCreate

async def create_user(db: AsyncSession, user_data: UserCreate) -> User:
    user = User(**user_data.model_dump())
    db.add(user)
    await db.commit()
    await db.refresh(user)
    return user

async def get_user(db: AsyncSession, user_id: int) -> User | None:
    result = await db.execute(select(User).where(User.id == user_id))
    return result.scalar_one_or_none()

async def get_users(db: AsyncSession, skip: int = 0, limit: int = 10):
    result = await db.execute(select(User).offset(skip).limit(limit))
    return result.scalars().all()

6. routers/users.py

复制代码
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_db
from schemas import UserCreate, UserResponse
import crud

router = APIRouter(prefix="/users", tags=["users"])

@router.post("/", response_model=UserResponse)
async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db)):
    return await crud.create_user(db, user)

@router.get("/{user_id}", response_model=UserResponse)
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
    user = await crud.get_user(db, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

@router.get("/", response_model=list[UserResponse])
async def list_users(skip: int = 0, limit: int = 10, db: AsyncSession = Depends(get_db)):
    return await crud.get_users(db, skip, limit)

7. main.py

复制代码
from fastapi import FastAPI
from contextlib import asynccontextmanager
from database import engine
from models import Base
from routers import users

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动时创建表
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield
    # 关闭时释放连接
    await engine.dispose()

app = FastAPI(lifespan=lifespan)
app.include_router(users.router)

@app.get("/")
async def root():
    return {"message": "Hello World"}

8. 运行

复制代码
pip install fastapi uvicorn sqlalchemy asyncpg pydantic[email]
uvicorn main:app --reload
相关推荐
风翼靓崽15 小时前
centos7以mysql为例自建yum源
数据库·mysql·adb
码农阿豪15 小时前
MongoDB迁移到金仓数据库:商品属性继续灵活存,订单报表交给 SQL
数据库·sql·mongodb
秋饼15 小时前
Redis 全面解析:从数据结构到主从/哨兵/集群、分布式锁与面试高频考点,代码到原理一次讲透
数据库·redis·分布式·面试·后端开发
秋名RG15 小时前
一条 SQL 语句在 MySQL 中的执行过程详解
数据库·sql·mysql
Canmag 兴隆磁性15 小时前
钕铁硼磁片取向方向检测自动化方案:从人工目检到流水线在线分选的工程实践
运维·数据库·自动化·工业自动化·钕铁硼·自动化检测·磁材料
yangmu320315 小时前
安全审批与日志审计:从“谁批准”到“谁执行”的全链路闭环
数据库
峯58015 小时前
一文学习FastAPI
学习·fastapi
R²AIN SUITE15 小时前
多云或私有云环境怎么选项目管理工具?RainSuite等5种产品的8项部署验证
服务器·网络·数据库
隔振降噪研究员16 小时前
裁断机一响,楼板为什么会跟着抖?
大数据·linux·数据库·人工智能
软件派17 小时前
生产动态数据智能风控:阈值判别、趋势预测、异常识别算法工程落地实战
前端·数据库·算法