Flask与FastAPI在异步处理上性能差异有多大?

文章导读
Flask 原生基于 WSGI,采用同步阻塞模型处理请求,每个请求会占用一个工作线程直到响应结束。FastAPI 基于 ASGI,原生支持异步协程,当处理 I/O 操作(如数据库查询、外部 API 调用)时,协程会自动让出 CPU 给其他任务。因此,在大量并发 I/O 场景下,FastAPI 能通过单线程处理数千个等待中的请求,而 Flask 需要大量线程或进程来维持并发,线程切换和内存开销会显著
📋 目录
  1. 先搞清楚性能差异从哪来
  2. 你的应用属于哪一类?先做这个判断
  3. 迁移时最容易踩的坑:数据库与第三方库
  4. 混合策略:同步与异步共存的风险边界
  5. 如何验证差异:用压测说话
A A

先搞清楚性能差异从哪来

Flask 原生基于 WSGI,采用同步阻塞模型处理请求,每个请求会占用一个工作线程直到响应结束。FastAPI 基于 ASGI,原生支持异步协程,当处理 I/O 操作(如数据库查询、外部 API 调用)时,协程会自动让出 CPU 给其他任务。因此,在大量并发 I/O 场景下,FastAPI 能通过单线程处理数千个等待中的请求,而 Flask 需要大量线程或进程来维持并发,线程切换和内存开销会显著增加响应延迟。这句话基本说清了核心差异:不是 Flask 慢,而是 Flask 在处理 I/O 密集型高并发时,资源利用率不如 FastAPI 高效。

你的应用属于哪一类?先做这个判断

先评估你的应用瓶颈在 CPU 还是 I/O。如果接口内包含多次数据库查询、文件读写或远程 HTTP 调用,且并发量超过几十,那么异步带来的性能提升会非常明显。一个简单的检查方法:在当前 Flask 路由函数中,如果每个请求平均等待外部资源的时间超过 50ms,且每秒请求数预期超过 100,那么换成 FastAPI 后很可能看到 5 倍以上的吞吐量提升。反之,如果计算密集型任务占主导,异步收益微乎其微,甚至因协程调度引入微小开销。这个判断方法可以在不写代码的情况下先估算收益。例如你的服务是一个数据库报表接口,需三次 SQL 查询,平均等待 80ms,目前用 Flask + 4 worker 勉强支撑 50 QPS,那 FastAPI 会明显改善。

Flask与FastAPI在异步处理上性能差异有多大?

迁移时最容易踩的坑:数据库与第三方库

从 Flask 迁移到 FastAPI 时,最容易忽略的是数据库连接池和 ORM 的异步支持。例如,使用 SQLAlchemy 时,若继续使用同步 Session,FastAPI 的异步端点中会阻塞事件循环,导致性能不升反降。正确做法是安装 asyncpg 或 aiomysql,配合 SQLAlchemy 的异步引擎,并在路由函数前加上 async def。另外,所有依赖的第三方库必须检查是否支持异步,否则需要运行在 run_in_executor 中,这会丧失大部分异步优势。检查第三方库是否支持异步:在仓库文档中搜索 "async" 或 "await",或者看 PyPI 标签。如果必须用同步库,建议在 FastAPI 中用 def 定义路由(会自动放线程池),但要注意线程池大小。具体配置如下:在 app 启动时添加 uvicorn.run(app, ...),或者通过 app = FastAPI() 后设置 app.state.limits?实际上线程池大小用 from starlette.concurrency import run_in_threadpool,但默认是 40。建议在 app 启动时显式设置:import asyncio; loop = asyncio.get_event_loop(); loop.set_default_executor(ThreadPoolExecutor(max_workers=8))(8 是 CPU 核心数两倍)。

混合策略:同步与异步共存的风险边界

不必将所有路由都改为异步。对于计算密集或不能完全异步的遗留代码,FastAPI 允许在一个应用中同时使用同步和异步端点。可以用 def 定义同步路由,FastAPI 会自动将其放入线程池运行。但注意:高并发同步端点会占用线程池资源,如果线程池耗尽(默认 40 个线程),后续请求将排队。建议在 app 启动时设定线程池大小为 CPU 核心数的 2 倍,并为关键异步接口保留隔离的线程池,避免相互影响。实际操作可以通过自定义 ThreadPoolExecutor 并注入到依赖中。例如,为同步路由设置单独的线程池:import concurrent.futures; sync_pool = concurrent.futures.ThreadPoolExecutor(max_workers=4),然后在同步端点中 await run_in_threadpool(sync_pool, your_sync_func)。注意:如果异步端点中调用了同步阻塞代码(比如 time.sleep(1)),会阻塞事件循环,必须用 await asyncio.sleep(1)

Flask与FastAPI在异步处理上性能差异有多大?

如何验证差异:用压测说话

使用 Locust 或 wrk 模拟真实场景的压力测试是唯一可靠的方法。编写两个完全相同的接口,一个用 Flask 同步实现,另一个用 FastAPI 异步实现,均连接同一个测试数据库。逐步增加并发用户数到 500,观察 P99 延迟和错误率。注意:测试环境应设置 keepalive 连接复用,避免连接建立开销放大差异。如果 Flask 版本出现大量 502 或超时,而 FastAPI 仍稳定,就证实了异步的收益。压测时记录两个指标:CPU 使用率和内存占用。FastAPI 在内存上通常更节省,因为线程数少。如果发现 FastAPI 的 worker 内存也飙升,检查是否误用了同步库导致线程膨胀。一条命令示例:wrk -t4 -c500 -d30s --latency http://localhost:8000/endpoint。注意先关闭日志输出,避免 I/O 干扰。