Web应用内存问题关键是定位泄漏或溢出链路:先看RSS曲线区分单次导出压垮、长连接堆积泄漏或pymalloc假象;导出必用StreamingHttpResponse+迭代器,数据库查加.iterator(),Nginx需关proxy_buffering,泄漏排查从tracemalloc和gc.get_objects()入手。

Web应用内存溢出不是“要不要优化”的问题,而是“哪条链路正在吃光内存”的定位问题。直接上 tracemalloc 或重启服务都治标不治本——得先分清是单次导出压垮内存,还是长连接/任务堆积引发持续上涨。
查内存暴涨曲线:先区分泄漏和溢出
打开监控看 RSS(Resident Set Size)曲线:
- 如果内存随请求量阶梯式跳升、每次请求后不回落 → 单次操作加载过大(比如
response.content读 GB 文件、pd.read_csv无分块) - 如果内存匀速爬升、重启归零、12 小时后爆掉 → 典型内存泄漏,重点查全局容器、循环引用、未关闭的数据库连接或 aiohttp session
- 如果内存涨到某个平台就卡住不动 → 可能是 pymalloc 内存池没还给系统,不是泄漏,但需确认对象生命周期是否合理
导出接口爆内存?立刻换 StreamingHttpResponse + 迭代器
用 HttpResponse 拼百万行 CSV 是最常见翻车点。它会把全部内容转成一个大 str,再 encode 成 bytes,中间至少两倍内存拷贝。
- 必须用
StreamingHttpResponse,传入生成器函数,每yield一行都得是bytes(不是str),例如b"foo,bar\r\n" - 数据库查询必须加
.iterator(chunk_size=2000),否则QuerySet仍全量缓存 - CSV 字段含双引号或换行符,必须按 RFC 4180 转义:字段整体加双引号,内部双引号变成两个(
"→"") - Excel 导出别硬扛:
openpyxl的write_only=True模式只省部分内存;超 5 万行直接切回 CSV,或让前端用 SheetJS 渲染
部署后流式响应卡住?Nginx 默认在背刺你
本地跑得飞快,一上生产就超时、延迟高、最后断连——90% 是 Nginx 把流式响应攒起来等满再发。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
立即学习“Python免费学习笔记(深入)”;
- 必须在
location块里关掉缓冲:proxy_buffering off; - 加上
proxy_buffer_size 128k;防止小响应被截断 - 如果用 Gunicorn,检查
--timeout是否够长(建议 ≥120),否则 worker 会在 yield 中途 kill 连接 - 别信“加了
Content-Length就能提速”——StreamingHttpResponse根本不能设这个 header,强行加会失效
查泄漏根源:从 gc.get_objects() 开始挖引用链
真泄漏往往藏在你看不见的地方:模块级字典、类属性缓存、aiohttp 的 ClientSession 实例没 close、甚至第三方库的 C 扩展层。
- 先用
tracemalloc.start(25)捕获峰值分配栈,tracemalloc.get_top_statistics("lineno")直接定位到哪行 new 了最多对象 - 怀疑某类对象泄漏?用
[o for o in gc.get_objects() if isinstance(o, YourClass)]数量是否只增不减 - 查谁在强引用它:
gc.get_referrers(obj)往上翻两层,重点关注全局变量、闭包、未清理的 weakref 回调 - 特别注意
aiohttp和aiomysql:旧版multidict(如 4.5.1)有已知泄漏,升级到 4.5.2+;ClientSession必须用async with或显式调.close()
最常被忽略的是:BOM 只对 CSV 有效,加在 Excel 文件头里会损坏二进制结构;而 yield 返回 str 不报错但客户端收不到数据——因为 StreamingHttpResponse 内部只接受 bytes。

















