Django内存暴涨主因是QuerySet误用、初始化阻塞和对象实例化开销。应避免print(qs)触发重复查询,复用.exists()/count(),优先values()而非only(),异步处理预热,用tracemalloc定位分配源头。

QuerySet 误用直接抬高内存峰值
最常见也最容易被忽略的内存暴涨点:你以为只查几条,其实 Django 已经把整张表对象全加载进内存。比如 print(qs) 看似无害,但会触发 __repr__() 查前 20 条——不进 _result_cache,后续 for obj in qs: 就是第二次全量查询;qs[5:10] 同样生成新 QuerySet 发 LIMIT/OFFSET,原 QuerySet 缓存仍为空。
- 调试时想看内容,用
list(qs[:10])或打开DEBUG=True看 SQL 日志,别依赖print(qs) - 同一逻辑必须复用变量:判断是否存在用
.exists(),要总数用.count(),避免构造完整 QuerySet -
values()返回字典,跳过模型实例化开销;only()仍创建完整 Model 实例,只是延迟字段加载——误用会引发 N+1 查询
Django 初始化逻辑阻塞就绪并吃光内存
K8s 中 Pod 频繁 OOMKilled 或 readiness probe 超时,90% 是因为 django.setup() 阶段同步执行了不该在启动时做的事:数据库连接池预热、缓存预热、第三方 SDK 初始化(如 OpenTelemetry)、AppConfig.ready() 里调 cache.get() 或 redis.ping()。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- DB 连接池设为
MAX_CONNS=0,让连接延迟到首次请求时创建 - 缓存预热改用
threading.Timer(5, lambda: cache.set_many(...)),5 秒后异步执行 - OpenTelemetry 关键环境变量:
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS="requests,sqlalchemy",关掉不需要的插件 - K8s YAML 的
command字段必须显式覆盖默认runserver,不能只改args
Python “一切皆对象” 在大规模数据下被放大
一个 int 在 C 里占 4 字节,在 Python 里空对象就 28 字节;列表 [1,2,3] 不存数字本身,而是存三个指向整数对象的指针。10 万条记录用 only('id'),内存仍是 values('id') 的 2–3 倍——因为每个实例仍携带引用计数、类型指针、_state、字段缓存字典等。
- 导出、统计、API 序列化前轻量查询,优先用
values('id', 'name'),不是only() -
values()和only()混用无意义:User.objects.only('username').values('id')中only()被静默丢弃 - 若需后续
save(),values()必须显式包含'id',否则无法定位记录
用 tracemalloc 定位真实内存分配源头
tracemalloc 是 Python 内置模块,能精确到行号告诉你哪段代码分配了多少内存。它比 top/pmap 更准,因为绕过了进程级粗粒度统计,直击对象分配路径。
- 启用方式简单:
import tracemalloc; tracemalloc.start() - 快照获取:
current, peak = tracemalloc.get_traced_memory(),单位是字节 - 查具体分配位置:
snapshot = tracemalloc.take_snapshot(),再用snapshot.statistics('lineno')排序看前 10 - 注意:仅用于诊断,生产环境勿长期开启,有性能损耗
cache.get() 卡住整个 WSGI 进程,或 values() 里漏掉 'id' 导致后续 save() 失败又 fallback 到全字段查询——这些细节不会报错,只会悄悄吃光内存。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










