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
相关推荐
j7~1 小时前
【仿 RabbitMQ 项目】发布订阅消息队列 (篇八):交换机数据管理模块,分层类设计、SQLite 回调原理与代码实现
服务器·网络·数据库·消息队列·交换机·交换机数据管理·sqlite回调原理
PHP实战开发录2 小时前
PHP上传文件为什么会被覆盖
数据库·mysql·php
SelectDB技术团队2 小时前
VARIANT 列接入网关 JSON 日志:建表、导入、子列查询与物化的完整步骤
大数据·数据库·人工智能·elasticsearch·搜索引擎·json·apache doris
涛思数据(TDengine)2 小时前
TDengine IDMP 2.0.2 发布:元素关系可视化、数据建模与联邦查询三大升级
大数据·数据库·人工智能·时序数据库·tdengine·工业互联网·工业ai
小米里的大麦2 小时前
17 MySQL 视图
数据库·mysql
ss2732 小时前
PG实战笔记 | 07 PostgreSQL常见编译项及其作用
数据库·笔记·postgresql
丨白色风车丨2 小时前
LangChain 1.0 核心用法速查:创建模型、创建 Agent、调用
数据库·langchain
临床数据科学和人工智能兴趣组2 小时前
正则表达式的基础语法的常用字符类
数据库·r语言·r语言-4.2.1·临床研究·临床试验
鱼很腾apoc2 小时前
【MySQL】 第2期 查询与函数系统详解:从基础 CRUD 到复合多表查询
linux·数据库·学习·mysql·dml