我们组前阵子搞性能优化,有个同事看了一篇博客说「async 比同步快十倍」,回来就把项目里所有函数都改成 async def。改完一压测,QPS 从 2000 掉到 800,他人都麻了。
这事其实很常见。async 不是银弹,它在某些场景能让 QPS 翻倍,在另一些场景反而拖后腿。我们花了一周时间,把 6 个典型场景都跑了一遍 benchmark,结论挺反直觉的。

测试环境先交代清楚
# 环境
# Python 3.11.5
# httpx 0.27 (异步 HTTP)
# requests 2.31 (同步 HTTP)
# 服务器:4 核 8G,Ubuntu 22.04
# 压测:wrk -t4 -c100 -d30s
# benchmark 模板
import asyncio
import time
import httpx
import requests
async def async_handler():
asyncwith httpx.AsyncClient() as client:
r= await client.get("http://api.internal/data")
returnr.json()
def sync_handler():
r= requests.get("http://api.internal/data")
returnr.json()
场景 1:纯 IO 等待(外部 HTTP 调用 10 个)
# 异步版:10 个请求并发
async def fetch_all_async():
asyncwith httpx.AsyncClient() as client:
tasks= [client.get(f"http://api/item/{i}") for i in range(10)]
results= await asyncio.gather(*tasks)
return[r.json() for r in results]
# 同步版:10 个请求顺序
def fetch_all_sync():
results= []
fori in range(10):
r= requests.get(f"http://api/item/{i}")
results.append(r.json())
returnresults
每个外部接口平均响应 80ms。
模式 | 平均延迟 | QPS |
async | 95ms | 1050 |
同步 | 810ms | 123 |
async 完胜 8.5 倍。这是 async 的甜点场景:大量 IO 等待,可以并发。
场景 2:纯 CPU 计算(算斐波那契 + JSON 序列化)
async def cpu_async():
result= fibonacci(35)
returnjson.dumps({"result": result, "ts": time.time()})
def cpu_sync():
result= fibonacci(35)
returnjson.dumps({"result": result, "ts": time.time()})
模式 | 平均延迟 | QPS |
async | 245ms | 408 |
同步 | 232ms | 432 |
同步反而快 5%。这是因为 async 有 event loop 调度开销,但函数里压根没有 await 点,event loop 占不到便宜。
教训:纯 CPU 密集别用 async,浪费。
场景 3:单次数据库查询(PostgreSQL)
# asyncpg
async def db_async():
conn= await asyncpg.connect(DSN)
row= await conn.fetchrow("SELECT * FROM users WHERE id = $1", 1)
awaitconn.close()
returndict(row)
# psycopg2
def db_sync():
conn= psycopg2.connect(DSN)
cur= conn.cursor()
cur.execute("SELECT* FROM users WHERE id = %s", (1,))
row= cur.fetchone()
conn.close()
returnrow
模式 | 平均延迟 | QPS |
async | 12ms | 8200 |
同步 | 13ms | 7600 |
打平。单次查询本身就快(局域网内 DB 响应 < 5ms),异步省不下多少。
场景 4:N+1 查询(典型 ORM 灾难)
# 同步版
def user_list_sync():
users= User.objects.all()[:50]
result= []
foru in users:
orders= Order.objects.filter(user_id=u.id)
result.append({"user":u.name, "orders": [o.id for o in orders]})
returnresult
# 异步版(asyncpg + 手工并发)
async def user_list_async():
users= await fetch_users(limit=50)
tasks= [fetch_orders(u.id) for u in users]
orders_list= await asyncio.gather(*tasks)
return[{"user": u.name, "orders": [o.id for o in orders]}
foru, orders in zip(users, orders_list)]
50 个 user,每个再查一次 orders。
模式 | 平均延迟 | QPS |
async | 65ms | 1530 |
同步 | 750ms | 133 |
async 快 11 倍。N+1 是 async 的另一个甜点。
但说实话,最佳解是把 N+1 改成 JOIN,那样同步也能跑到 1800 QPS,比 async N+1 还快。async 不该用来掩盖糟糕的查询设计。
场景 5:读文件(本地小文件)
import aiofiles
async def read_async():
asyncwith aiofiles.open('/data/config.json', 'rb') as f:
content= await f.read()
returnjson.loads(content)
def read_sync():
withopen('/data/config.json', 'rb') as f:
content= f.read()
returnjson.loads(content)
模式 | 平均延迟 | QPS |
async | 2.8ms | 35000 |
同步 | 1.5ms | 65000 |
同步快近一倍。原因是 Linux 上文件 IO 没有真正的异步实现,aiofiles 内部其实是用线程池模拟的。多了一层线程切换开销。
教训:读本地文件别用 async,老老实实同步。
场景 6:混合负载(HTTP + DB + 简单计算)
async def mixed_async(user_id):
user= await fetch_user_from_db(user_id)
profile= await fetch_external_profile(user.id)
score= calculate_score(user, profile)# CPU
awaitsave_score_to_db(user_id, score)
returnscore
模式 | 平均延迟 | QPS |
async | 145ms | 680 |
同步 | 280ms | 357 |
async 快 1.9 倍。这才是大部分 Web 接口的真实场景:有 IO 也有 CPU,async 因为能在 IO 等待时让出执行权,整体吞吐能上去,但不会快到 8 倍那种夸张程度。
结论:什么时候该用 async
实测结论摆这儿,我自己总结的判断标准:
场景 | 该不该用 async |
多个外部 IO 可以并发 | 用,效益最大 |
单次 IO 完事就返回 | 用啥都一样 |
纯 CPU | 别用,反而慢 |
本地文件 | 用同步,aiofiles 是假异步 |
写 API 接口(混合负载) | 用,一般快 1.5~2 倍,值得 |
还有个隐藏成本得提:async 代码的调试和异常追踪比同步难很多。asyncio.gather 里抛了异常 traceback 经常一团乱,新人排查能折腾半天。如果你的接口本来就够快了,没必要为了 50% 的性能提升把整个团队拖进 async 的复杂度泥坑。
那位把所有函数改成 async 的同事,后来全回滚了。他自己总结的一句话挺到位:「async 是工具不是宗教」。