Python中有关Tornado框架的QPS性能问题如何优化?

机子:  1c 2g hdd 硬盘

python: 3.5.3

必要的 redis 和 mysql 都跑在同在一台机子上

今天用 ab 测试了一个登陆接口,分别进行 3 个数据库操作: select, replace, update ;最后会将产生的 token 扔进 redis 里。

数据库操作用了 tormysql,redis 直接是官方库,没有使用异步操作。

测试结果让我有点不可思议,居然才 30 - 35 QPS。

单纯测试 hello world,大概是 1000 左右。

不知道这个结果是否正常?


Python中有关Tornado框架的QPS性能问题如何优化?

16 回复

是否异步 数据索引等是否处理好?
这两点是单进程性能的关键点 另外就是正式部署一般是 nginx +多个 tornado 这样才能算是测试出
单机的真正的服务能力,如果和其他语言 web 框架对比 合适的数据还是应该要累加下多个进程总共的 QPS。


要优化Tornado的QPS,核心就两点:减少阻塞和榨干CPU。

1. 异步化一切I/O操作 这是最重要的。所有数据库查询、外部API调用、文件读写都必须用异步客户端。别用同步库,一个请求卡住整个事件循环就完了。

# 错误:用同步requests库,会阻塞
import requests
def get_data():
    return requests.get('http://api.example.com').json()

# 正确:用aiohttp或Tornado自己的AsyncHTTPClient
async def get_data():
    http_client = AsyncHTTPClient()
    response = await http_client.fetch('http://api.example.com')
    return json.loads(response.body)

2. 调优Tornado配置

# 启动时这样配置
app = make_app()
server = HTTPServer(app)
server.bind(8888)
# 关键:进程数配CPU核数,充分利用多核
server.start(0)  # 0表示自动根据CPU核心数启动子进程
IOLoop.current().start()

3. 数据库连接池 用aiomysql、asyncpg这些真正的异步驱动,别用同步ORM的伪异步接口。

4. 压测找瓶颈wrklocust压测,看top命令哪个进程CPU高,用py-spy抓火焰图定位热点函数。

简单说就是:全异步+多进程+真异步驱动。



数据库都建索引了。数据库的每个操作在 0.002 秒。

除了 redis 这块,其他都异步了。这里只放 token 的登陆信息。

测试时,只跑了一个 tornado 进程。

开两个的话,能到 50 左右。

应该是哪里阻塞了,同步框架也能随便达到这个 QPS



把一个 5 秒的 timer 关了之后。

单进程的 tornado 去到了 50qps。

然后之前没有看清楚,发现数据库的操作要花 200 毫秒了…

平均每个请求 500-600 毫秒。

看来瓶颈应该是数据库了。

单线程操作数据库,IO 同步,不卡才怪?



大哥,tormysql 是异步库。

内部实现貌似是用 greenlet 去执行数据库操作。

如果 mysql 是异步 IO 了,那我认为你应该优先怀疑你的测试方法,串行测试 QPS 取决于访问 mysql 的往返延迟,你应该用并行请求才对。



是的,应该是我测试方法错了。

用同一个账号登陆的。

发现更新这个账号的信息是最耗时的。

python 搞高性能、高并发本来就不行,要是再加个锁之类那就更玩完儿了

登录测试用不同账号试试,用同一个账号小心交叉锁

这个配置,hello world 才 1000 ?有点底呀



我也觉得…

Hello world 级别的 Benchmark 直接看 https://www.techempower.com/benchmarks/#section=data-r14&hw=ph&test=plaintext 这种就好了

这种 QPS 低只有卡在数据库上了。redis 官方库可以用 pool,那个 mysql 库就没用过了

tormysql 有使用连接池么?可以看下 MySQL 的 cpu 消耗



已经用了连接池,最大链接是 60。

replace 以及 update 两条语句都用了不同的事务去执行。

发现用事务执行的语句消耗的时间都比较长。

回到顶部