Python FastAPI 高性能 API 开发:三个核心优化方向

用 Python 做后端 API,FastAPI 基本是现在的首选------原生异步支持、自动生成接口文档、开发效率高。但不少项目跑起来之后,并发一上来响应就变慢,很多时候不是框架不行,是架构和细节没处理好。本文从三个实战维度,结合可直接复用的代码,讲讲怎么把 FastAPI 服务的性能做扎实。

一、路由模块化拆分,依赖按需注入

很多新手写 FastAPI 喜欢把所有路由都堆在启动文件里,鉴权、日志、参数校验这些依赖全局全量挂载。项目小的时候看不出问题,接口一多,不仅代码臃肿难维护,每个请求都要走一遍无关的中间件逻辑,平白增加响应耗时。

优化思路很直接:按业务域拆分路由模块,依赖只在需要的路由层级注入,不要搞"全局一刀切"。

先按模块拆分路由文件,比如用户、订单各占一个文件,用 APIRouter 管理:

python 复制代码
# app/routers/user.py
from fastapi import APIRouter, Depends
from ..dependencies.auth import verify_token
from ..services.user import get_user_by_id

# 定义用户模块路由,统一前缀和标签
router = APIRouter(prefix="/user", tags=["用户接口"])

# 仅当前模块的所有接口注入鉴权依赖,不影响其他路由
router.dependencies = [Depends(verify_token)]

@router.get("/{user_id}")
async def get_user_info(user_id: int):
    user = await get_user_by_id(user_id)
    return {"code": 0, "data": user}

主启动文件只做路由挂载和全局异常兜底,逻辑保持清爽:

python 复制代码
# app/main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from .routers import user, order

app = FastAPI(title="业务API服务")

# 按业务模块挂载路由,统一/api前缀
app.include_router(user.router, prefix="/api")
app.include_router(order.router, prefix="/api")

# 全局统一异常处理,避免业务异常直接抛出堆栈
@app.exception_handler(Exception)
async def global_error_handler(request: Request, exc: Exception):
    # 生产环境替换为日志组件记录错误详情
    print(f"请求异常 [{request.method} {request.url.path}]: {str(exc)}")
    return JSONResponse(
        status_code=500,
        content={"code": 500, "msg": "服务内部错误"}
    )

这样拆分之后,不仅代码可维护性变强,每个请求只会经过对应路由的依赖,减少了无效的逻辑执行。

二、异步数据库连接池 + 并行查询

Python API 的性能瓶颈,十有八九出在数据库 IO 上。FastAPI 的异步优势,必须搭配异步数据库驱动才能完全发挥;如果还用同步驱动、每次请求新建连接,再强的服务器也扛不住并发。

核心要做两件事:用连接池复用数据库连接,减少连接建立的开销;对无依赖的查询做并行执行,缩短总响应时间。

首先配置异步数据库连接池,这里以 MySQL + SQLAlchemy 异步版为例:

python 复制代码
# app/db/database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

# 异步数据库连接串,使用 aiomysql 驱动
DATABASE_URL = "mysql+aiomysql://root:your_password@127.0.0.1:3306/biz_db"

# 创建异步引擎,配置连接池参数
engine = create_async_engine(
    DATABASE_URL,
    pool_size=20,        # 常驻连接数,按服务器CPU核数调整
    max_overflow=10,     # 峰值时可额外创建的临时连接数
    pool_recycle=3600,   # 连接1小时后自动回收,避免超时断开
    echo=False           # 生产环境关闭SQL日志
)

# 异步会话工厂
AsyncSessionLocal = sessionmaker(
    engine, class_=AsyncSession, expire_on_commit=False
)

# 依赖注入用的数据库会话生成器
async def get_db():
    async with AsyncSessionLocal() as session:
        yield session

业务层对无依赖的查询用 asyncio.gather 并行执行,总耗时等于最慢的那个查询,而不是串行累加:

python 复制代码
# app/services/user.py
import asyncio
from sqlalchemy import text
from ..db.database import AsyncSessionLocal

async def get_user_detail(user_id: int):
    async with AsyncSessionLocal() as db:
        # 两个查询无依赖关系,并行执行
        user_query = db.execute(
            text("SELECT id, username, email FROM users WHERE id = :uid"),
            {"uid": user_id}
        )
        order_query = db.execute(
            text("SELECT id, amount FROM orders WHERE user_id = :uid LIMIT 10"),
            {"uid": user_id}
        )
        
        user_res, order_res = await asyncio.gather(user_query, order_query)
        
        return {
            "user": user_res.mappings().first(),
            "recent_orders": order_res.mappings().all()
        }

三、热点数据缓存 + 响应压缩

对于读多写少的接口,缓存是提升 QPS 最见效的手段;再配合响应压缩,能进一步降低网络传输耗时,提升用户侧的响应速度。

单实例场景用内存缓存足够简单,多实例可以换成 Redis。这里实现一个轻量的内存缓存装饰器,搭配 FastAPI 自带的 GZip 压缩中间件。

python 复制代码
# app/main.py
from fastapi import FastAPI
from starlette.middleware.gzip import GZipMiddleware
import time
from functools import wraps

app = FastAPI()

# 全局启用GZip压缩,大于1KB的响应自动压缩
app.add_middleware(GZipMiddleware, minimum_size=1024)

# 简易内存缓存装饰器,支持设置过期时间
def memory_cache(expire_seconds: int = 60):
    cache_store = {}
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            # 以函数参数生成缓存key,实际项目可结合请求路径优化
            cache_key = f"{func.__name__}:{str(args)}:{str(sorted(kwargs.items()))}"
            # 命中缓存且未过期直接返回
            if cache_key in cache_store:
                data, expire_time = cache_store[cache_key]
                if time.time() < expire_time:
                    return data
            # 未命中则执行原函数并写入缓存
            result = await func(*args, **kwargs)
            cache_store[cache_key] = (result, time.time() + expire_seconds)
            return result
        return wrapper
    return decorator

# 模拟数据库查询
@memory_cache(expire_seconds=30)
async def query_hot_articles():
    # 实际业务中此处为数据库查询
    return [{"id": i, "title": f"热点文章{i}"} for i in range(10)]

# 热点列表接口,加30秒缓存
@app.get("/api/hot-articles")
async def hot_articles():
    data = await query_hot_articles()
    return {"code": 0, "data": data}

对于不常变更的静态数据,还可以在响应头里加 Cache-Control,让客户端和CDN直接缓存,进一步减轻服务器压力。

最后

FastAPI 的性能优化不用搞太多花里胡哨的操作,把路由拆干净、数据库连接复用好、热点数据加上缓存,绝大多数业务场景的性能都足够用。优化前建议先拿压测工具跑一遍,定位真实瓶颈再动手,避免上来就全量改造,徒增维护成本。

相关推荐
用户9194099810741 天前
别再一条条写库了:个人微信社群消息的定时同步与冷热存储避坑指南
api
Java研究者2 天前
AI智能体研发 | 什么是OpenAI API协议
人工智能·大模型·openai·api·agent·智能体
Alan_754 天前
高并发架构优化实战:Redis 调优、数据库扩展与协同架构三大核心模块
api
曲幽4 天前
你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南
python·fastapi·web·graphql·route·cors·rest·strawberry
CaffeinePro7 天前
依赖注入:FastAPI最核心的解耦能力案例解析
后端·fastapi
用户7783366132119 天前
从 Serper 切到 SERP API:200 行代码 diff 实战
api
曲幽10 天前
刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API
python·fastapi·web·translate·goldendict·libretranslate·stardict·pystardict
CaffeinePro14 天前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
jay神16 天前
基于 FastAPI + Vue 的宠物领养管理系统
前端·vue.js·python·毕业设计·fastapi·宠物