能,但需满足两个前提:django 进程以 debug=false 运行,且 py-spy 具备对应进程读权限;开发时 runserver 默认禁用 ptrace,生产环境用 gunicorn/uwsgi 更稳定。

Py-Spy 能直接 attach 到 Django 进程吗?
能,但必须满足两个前提:Django 进程以非调试模式(DEBUG=False)运行,且 Py-Spy 有对应进程的读权限(通常需同用户或 root)。开发时用 runserver 启动的进程默认禁用 ptrace,会报 Permission denied 或卡在 Waiting for process to start...。生产环境用 gunicorn 或 uWSGI 启动时更稳定。
- 检查进程是否可被追踪:
cat /proc/<pid>/status | grep CapBnd</pid>,若含0000000000000000表示无CAP_SYS_PTRACE权限 - 临时修复(仅测试):
sudo setcap cap_sys_ptrace+ep $(which py-spy) - 推荐方式:用
py-spy record -p <pid> -o profile.svg</pid>替代top模式,减少实时干扰
Django 视图里 sleep(1) 看不到?Py-Spy 采样频率怎么调
Py-Spy 默认每 100ms 采样一次,短于 50ms 的阻塞(比如小量数据库查询、模板渲染)容易漏掉。Django 中人为 time.sleep(1) 理论上可见,但如果视图响应太快(
- 提高采样率:
py-spy record -p <pid> --duration 30 --rate 1000 -o profile.svg</pid>(每 1ms 一次,慎用,CPU 开销明显上升) - 确认采样生效:用
py-spy top -p <pid></pid>实时看,观察django.core.handlers.wsgi.WSGIHandler.__call__是否频繁出现在栈顶 - 注意:
--rate超过 1000 可能导致采样失真,尤其高并发下
profile.svg 里全是 select.epoll_wait 或 pthread_cond_wait 怎么办
这是典型 I/O 等待占主导的信号——Django 进程大部分时间在等网络请求、数据库响应或文件读写,不是 Python 代码本身慢。Py-Spy 抓到的是“等待态”,不代表这些函数是瓶颈根源。
- 先查外部依赖:
SHOW PROCESSLIST(MySQL)、pg_stat_activity(PostgreSQL)看慢查询 - 检查中间件:如
django.contrib.sessions.middleware.SessionMiddleware配了数据库后端,每次请求都触发 DB 查询 - 对比
py-spy record和py-spy dump -p <pid></pid>输出:后者显示当前所有线程栈,可识别是否卡在某个锁或连接池耗尽
为什么 gunicorn worker 用 sync 模式比 gevent 更容易被 Py-Spy 捕获
因为 gevent 使用协程 + epoll,在单线程内调度大量 greenlet,Py-Spy 的基于 OS 线程栈的采样机制无法准确映射 Python 层调用链;而 sync 模式每个 worker 是独立 OS 线程,栈帧清晰,py-spy 能完整抓到从 WSGI 入口到视图函数的路径。
- 用
gunicorn --worker-class sync --workers 2启动更利于诊断 - 若必须用
gevent,改用py-spy record --subprocesses并配合--duration加长采样窗口,提高捕获概率 -
py-spy对async/await视图支持有限,await后的挂起点常显示为coroutine.send,需结合asyncio日志交叉验证
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











