Python进程CPU持续超80%多为应用层逻辑问题,需用top -H和ps -T定位高负载线程,再以py-spy record生成火焰图分析热点函数,并结合vmstat、iostat等排除I/O阻塞误判。

top 看到 Python 进程 CPU 占用持续超 80%,基本可以断定是应用层逻辑问题,不是系统或容器配置导致的假性飙升。先别改代码,按顺序查清楚线程、函数、调用链。
用 top 和 ps -T 定位高负载线程
Python 多线程程序里,CPU 飙高往往只发生在个别线程上,top 默认只显示进程级汇总,容易误判为“整个服务都卡”。必须下钻到线程粒度:
- 运行
top -p <pid></pid>,按H键切换线程视图(显示 TID) - 记下 CPU% 最高的那个 TID(比如
12345) - 执行
ps -T -p <pid> | grep 12345</pid>,确认该线程状态是否为R(running),排除它只是短暂抖动 - 把 TID 转成十六进制:
printf "%x\n" 12345,结果如3039,后续分析堆栈要用
用 py-spy record 抓实时火焰图
cProfile 要改代码或重启进程,不适用于生产环境;py-spy 是唯一能无侵入采样运行中 Python 进程的工具,且支持多线程/异步:
- 安装:
pip install py-spy(最好在目标机器上装,避免权限或路径问题) - 采样 30 秒:
py-spy record -p <pid> -o profile.svg -d 30</pid> - 打开生成的
profile.svg,找最宽、顶部平直的函数块——那就是 CPU 时间真正花掉的地方 - 特别注意:如果火焰图里大量出现
select、epoll_wait或sleep,说明不是 CPU 密集型,而是 I/O 阻塞被错误识别为“高 CPU”,需结合wa(iowait)指标交叉验证
检查常见 CPU 密集型陷阱代码
火焰图定位到函数后,重点排查这几类高频出问题的写法:
- 死循环没退出条件:
while True:里没break或time.sleep(),尤其在后台线程或信号处理中 - 同步阻塞调用被误放在线程池里:
requests.get()在concurrent.futures.ThreadPoolExecutor中大量并发,GIL 不起作用,但线程数过多会引发调度开销和锁竞争 - JSON 序列化大数据:
json.dumps(big_dict)在响应中直接调用,比orjson.dumps()慢 3–5 倍,且是纯 CPU 操作 - 正则反复编译:
re.match(r'\d+', s)在循环里没预编译,应改为pattern = re.compile(r'\d+')提前定义 - 字符串拼接滥用:
s += part在长循环中,每次触发新对象分配,换成''.join(parts_list)
区分 CPU 密集 vs I/O 密集误判
很多“CPU 飙高”其实是 top 显示误导:当大量线程卡在系统调用(如 read、connect)上时,内核会把它标记为 R(running)态而非 S(sleeping),导致 %CPU 数值虚高。这时要查:
立即学习“Python免费学习笔记(深入)”;
- 运行
vmstat 1,看cs(context switch)是否异常高,同时sy(system CPU)占比大 → 锁竞争或频繁系统调用 - 运行
iostat -x 1,看%util和await,确认是不是磁盘 I/O 拖慢了整个线程调度 - 用
strace -p <pid> -e trace=network,io</pid>抓系统调用,看是否卡在recvfrom或writev上 - 如果是 ASGI 服务(如 Uvicorn),检查是否混用了同步数据库驱动(如
psycopg2)而没配asyncpg,会导致事件循环被阻塞


















