当前位置:首页>python>Python async 到底什么时候快,我们测了 6 个场景

Python async 到底什么时候快,我们测了 6 个场景

  • 2026-09-03 15:15:03
Python async 到底什么时候快,我们测了 6 个场景

我们组前阵子搞性能优化,有个同事看了一篇博客说「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 是工具不是宗教」。

最新文章

随机文章