异步任务延迟飙高主因是IO卡住,非代码慢:需换真异步驱动(如asyncpg/aiomysql)、复用Redis连接池、禁用同步调用(time.sleep/requests),并为所有外部IO显式设asyncio.wait_for超时。

延迟飙高,八成不是代码慢,而是异步任务在某处“卡住不动”——最常见的是数据库驱动没换异步版、Redis连接未复用、或协程里混进了同步阻塞调用。
查 asyncio.wait_for 是否被滥用或漏设
很多开发者以为加了 async 就万事大吉,结果关键 IO 操作(比如调第三方 API、查 Redis)没包 asyncio.wait_for,一卡就是几秒甚至几十秒,拖垮整个事件循环。
- 所有外部 HTTP 请求必须用
aiohttp.ClientSession+ 显式timeout参数,不能依赖全局默认值(默认是永不超时) - Redis 调用如
aioredis.Redis.get()本身不带超时,得自己套一层:await asyncio.wait_for(redis.get("key"), timeout=1.5) - 避免在协程里直接调
time.sleep()或requests.get()—— 这些会彻底阻塞事件循环 - 检查日志里是否频繁出现
asyncio.TimeoutError:有,说明超时机制起了作用;没有,反而更危险——可能根本没设超时,请求在后台无限挂起
确认数据库驱动是否真异步
FastAPI 标着 “async”,但如果你用的是 psycopg2 或 mysql-connector-python 这类同步驱动,async def 端点只是“假异步”:协程一执行到 cursor.execute() 就停住,整个事件循环被锁死。
- PostgreSQL 必须换
asyncpg(不是psycopg2),连接字符串以postgresql+asyncpg://开头 - MySQL 推荐
aiomysql或asyncmy,别用PyMySQL+asyncio.to_thread打补丁——性能差、易出错 - SQLAlchemy 用户注意:
create_async_engine()才是真异步,create_engine()即使配了future=True也还是同步的 - 用
SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active'(PG)或redis-cli info | grep connected_clients(Redis)实时看连接是否堆积,堆了就说明驱动没真正异步化
看 uvloop 是否启用 & 事件循环是否被污染
CPython 默认事件循环性能一般,uvloop 能提升 2–3 倍吞吐。但更隐蔽的问题是:有人在协程里调了 threading.Thread().start() 或用了 multiprocessing,这些操作会偷偷把事件循环“污染”成多线程模式,导致协程调度失效、延迟毛刺频发。
立即学习“Python免费学习笔记(深入)”;
- 启动服务时加
import uvloop; asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - 禁止在协程中创建新线程;必须用线程的场景,改用
asyncio.to_thread()(Python 3.9+)并严格限制并发数 - 检查是否有模块在 import 时就执行了耗时同步操作(比如某些 SDK 初始化加载大配置文件),这会让第一个请求特别慢
- 用
asyncio.all_tasks()在压测中打印当前活跃任务数,如果长期 >1000 且不下降,大概率有协程泄漏(比如忘了await某个 Future)
Redis 连接池是否共享且大小合理
异步服务里每个请求都新建 aioredis.Redis(host="..."),等于每请求建一个连接池,内存暴涨、FD 耗尽、延迟飙升——这不是并发高,是资源炸了。
- Redis 实例必须全局单例复用:
redis = await aioredis.from_url("redis://...")放在模块顶层或依赖注入容器里 - 连接池大小建议设为
minsize=10, maxsize=50(根据并发量调整),别用默认的minsize=1—— 高并发下抢不到连接就会排队等 - 检查错误日志里有没有
ConnectionClosedError或Max number of connections reached,这是连接池打满的直接证据 - 用
redis-cli client list | wc -l看真实连接数,如果远高于你设的maxsize,说明连接没正确释放(常见于异常路径没进finally或async with)
真正卡住异步服务的,往往不是“哪段代码慢”,而是“哪条路没走通”——连接没复用、超时没兜底、驱动没换真异步、事件循环被悄悄切走。这些点不逐个验证,光加 worker 数或调优 GC,只会让问题更难定位。


















