1. 项目概述
FastAPI+SQLAlchemy+pymysql这套技术栈正在成为Python后端开发的热门选择。作为一名长期使用Django的开发者,我第一次接触这套组合时就被它的简洁高效所吸引。相比Django ORM,SQLAlchemy提供了更灵活的数据库操作方式,而FastAPI的异步特性则让接口性能有了显著提升。
这个教程将带你完整走通从环境搭建到CRUD接口实现的全部流程。我们会从最基础的MySQL安装配置开始,逐步构建一个具备完整增删改查功能的FastAPI应用。过程中我会分享许多实际项目中积累的经验,比如如何避免常见的连接池问题、事务处理的正确姿势等。
2. 环境准备
2.1 MySQL安装与配置
MySQL的安装看似简单,但配置不当会导致后续开发中各种奇怪的问题。以Windows平台为例,推荐使用MySQL Installer进行安装,它能自动处理依赖和环境变量问题。
安装完成后,这几个配置项需要特别注意:
code复制[mysqld]
default_authentication_plugin=mysql_native_password
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
提示:utf8mb4字符集是必须的,它能完整支持emoji等特殊字符,避免未来出现编码问题。
2.2 Python环境搭建
建议使用Python 3.8+版本,并创建独立的虚拟环境:
bash复制python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
安装核心依赖包:
bash复制pip install fastapi sqlalchemy pymysql uvicorn
3. 项目结构设计
良好的项目结构能显著提升代码可维护性。这是我经过多个项目验证的推荐结构:
code复制/project
/app
/models
__init__.py
base.py
user.py
/schemas
user.py
/crud
user.py
/api
user.py
database.py
config.py
main.py
这种结构分离了数据模型、业务逻辑和接口层,当项目规模扩大时依然能保持清晰。每个文件都有明确的职责:
- models/: 定义SQLAlchemy数据模型
- schemas/: Pydantic模型,用于请求/响应验证
- crud/: 数据库操作封装
- api/: FastAPI路由定义
4. 数据库连接配置
4.1 创建数据库连接
在database.py中配置核心连接逻辑:
python复制from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "mysql+pymysql://user:password@localhost:3306/dbname"
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
pool_size=20,
max_overflow=100,
pool_timeout=30,
pool_recycle=3600
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
注意:连接池参数需要根据实际负载调整。pool_recycle特别重要,可以避免MySQL默认8小时断开连接导致的问题。
4.2 实现依赖注入
FastAPI的依赖注入系统与SQLAlchemy完美配合:
python复制from fastapi import Depends
from sqlalchemy.orm import Session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# 在路由中使用
@app.get("/users")
async def read_users(db: Session = Depends(get_db)):
return db.query(User).all()
这种模式确保了每个请求结束后数据库连接会被正确关闭,避免连接泄漏。
5. 数据模型定义
5.1 基础模型设计
在models/base.py中定义所有模型共享的基类:
python复制from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import as_declarative
@as_declarative()
class Base:
id = Column(Integer, primary_key=True, index=True)
5.2 用户模型示例
在models/user.py中定义具体的业务模型:
python复制from sqlalchemy import Column, String, DateTime
from .base import Base
class User(Base):
__tablename__ = "users"
username = Column(String(50), unique=True, index=True)
email = Column(String(100), unique=True, index=True)
hashed_password = Column(String(100))
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, onupdate=func.now())
模型设计时的几个关键点:
- 索引字段:查询频繁的字段应添加index=True
- 唯一约束:唯一性字段需要unique=True
- 自动时间戳:created_at/updated_at是审计必备字段
6. CRUD操作实现
6.1 创建操作
在crud/user.py中实现创建逻辑:
python复制from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException
def create_user(db: Session, user_data: dict):
db_user = User(**user_data)
try:
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
except IntegrityError as e:
db.rollback()
raise HTTPException(
status_code=400,
detail="Username or email already exists"
)
经验:一定要捕获IntegrityError并手动回滚,否则可能导致事务处于不一致状态。
6.2 查询操作
实现各种查询场景:
python复制def get_user(db: Session, user_id: int):
return db.query(User).filter(User.id == user_id).first()
def get_user_by_email(db: Session, email: str):
return db.query(User).filter(User.email == email).first()
def list_users(db: Session, skip: int = 0, limit: int = 100):
return db.query(User).offset(skip).limit(limit).all()
查询优化的几个技巧:
- 使用.first()而非.all()[0]获取单条记录
- 分页查询一定要用offset/limit组合
- 复杂查询考虑使用.select_from()优化
7. FastAPI接口开发
7.1 路由定义
在api/user.py中定义用户相关接口:
python复制from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from .. import schemas, crud
from ..database import get_db
router = APIRouter(prefix="/users", tags=["users"])
@router.post("/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
return crud.create_user(db=db, user_data=user.dict())
@router.get("/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
7.2 请求验证
使用Pydantic模型进行数据验证:
python复制from pydantic import BaseModel, EmailStr
class UserBase(BaseModel):
username: str
email: EmailStr
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
created_at: datetime
class Config:
orm_mode = True
orm_mode=True允许Pydantic模型直接从ORM对象加载数据,这是FastAPI与SQLAlchemy集成的重要特性。
8. 高级特性实现
8.1 异步数据库访问
FastAPI的异步特性可以大幅提升并发能力。首先需要安装异步驱动:
bash复制pip install asyncpg aiomysql
然后修改数据库配置:
python复制from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
ASYNC_DB_URL = "mysql+aiomysql://user:password@localhost:3306/dbname"
async_engine = create_async_engine(ASYNC_DB_URL)
AsyncSessionLocal = sessionmaker(
async_engine, class_=AsyncSession, expire_on_commit=False
)
8.2 事务管理
复杂业务通常需要事务支持:
python复制async def transfer_funds(db: AsyncSession, from_id: int, to_id: int, amount: float):
async with db.begin():
from_account = await db.get(Account, from_id)
to_account = await db.get(Account, to_id)
if from_account.balance < amount:
raise ValueError("Insufficient funds")
from_account.balance -= amount
to_account.balance += amount
await db.commit()
9. 性能优化技巧
9.1 连接池调优
SQLAlchemy连接池的默认配置可能不适合生产环境,建议调整:
python复制engine = create_engine(
SQLALCHEMY_DATABASE_URL,
pool_size=20, # 保持的连接数
max_overflow=100, # 允许超过pool_size的连接数
pool_timeout=30, # 获取连接的超时时间(秒)
pool_recycle=3600, # 连接回收时间(秒)
pool_pre_ping=True # 执行前检查连接是否有效
)
9.2 查询优化
避免N+1查询问题:
python复制# 不好的写法:会产生N+1查询
users = db.query(User).all()
for user in users:
print(user.posts) # 每次循环都会查询数据库
# 好的写法:使用joinedload预加载关联数据
from sqlalchemy.orm import joinedload
users = db.query(User).options(joinedload(User.posts)).all()
10. 常见问题排查
10.1 连接超时问题
症状:间歇性出现"MySQL server has gone away"错误。
解决方案:
- 检查MySQL的wait_timeout设置(建议设置为8小时)
- 配置SQLAlchemy的pool_recycle小于wait_timeout
- 启用pool_pre_ping=True
10.2 字符集问题
症状:存储emoji或特殊字符时出现乱码。
确保以下几点:
- 数据库、表和字段都使用utf8mb4字符集
- 连接字符串中添加?charset=utf8mb4参数
- SQLAlchemy配置中指定编码:
python复制create_engine("mysql+pymysql://...", connect_args={"charset": "utf8mb4"})
11. 项目部署建议
11.1 生产环境配置
生产环境需要考虑:
- 使用环境变量管理敏感配置
- 启用数据库连接池监控
- 配置合理的日志记录
推荐使用python-dotenv管理环境变量:
python复制from dotenv import load_dotenv
load_dotenv()
DB_URL = f"mysql+pymysql://{os.getenv('DB_USER')}:{os.getenv('DB_PASS')}@{os.getenv('DB_HOST')}/{os.getenv('DB_NAME')}"
11.2 性能监控
集成Prometheus监控:
python复制from prometheus_fastapi_instrumentator import Instrumentator
app = FastAPI()
Instrumentator().instrument(app).expose(app)
这套组合在实际项目中表现非常出色。我在一个中等规模的电商项目中采用这种架构,轻松支撑了每秒1000+的并发请求。关键在于合理配置连接池和充分利用FastAPI的异步特性。
