直接查看慢查询日志和connection.queries可立即定位90%性能问题;开发环境用connection.queries或LOGGING+DEBUG=True捕获SQL及毫秒级耗时,筛选time>0.1s、SELECT *、含Subquery或嵌套annotate的语句;索引应覆盖WHERE/JOIN/ORDER BY/GROUP BY字段组合,外键必加索引,高频组合查用Meta.indexes建复合索引;select_related仅适用于正向ForeignKey/OneToOneField,prefetch_related慎用于大数据集,避免IN超长或重复count();聚合计算优先用直接Sum或分步处理,禁用低效链式values/annotate;所有优化前必用explain验证执行计划。

直接看慢查询日志和 connection.queries,别猜 —— 90% 的性能问题能立刻定位到具体 SQL 和执行时间。
怎么快速抓到真正拖慢接口的那条 SQL
别依赖前端响应时间或模糊感觉。Django 提供了最轻量、最准的现场诊断方式:
- 在视图或管理命令里临时加
from django.db import connection,执行完查询后打印connection.queries,每条记录含sql、time(秒)、vendor - 配合
LOGGING配置开启django.db.backends的DEBUG=True,所有 SQL 会输出到 console 或文件,带毫秒级耗时 - 重点筛出
time> 0.1s 的语句,尤其是重复出现、SELECT *、含Subquery或多层嵌套annotate的 —— 这些大概率是索引缺失或写法失当的信号 - 注意:
connection.queries在DEBUG=False下为空,仅用于开发/预发环境;生产排查请用数据库原生慢日志(如 MySQL 的slow_query_log)
哪些字段加索引最有效,db_index 和 Meta.indexes 怎么选
索引不是越多越好,关键看 WHERE、JOIN、ORDER BY、GROUP BY 中实际参与的字段组合。常见误判是只给单字段加 db_index=True,但查询条件是多字段联合过滤。
- 外键字段必须加索引:如
commune = models.ForeignKey(..., db_index=True),否则filter(commune=...)就是全表扫描 - 日期+外键组合高频查询(如气象数据按地区+时间范围聚合),必须建复合索引:
models.Index(fields=['commune', 'date'])和['date', 'commune']都要,覆盖不同查询顺序 -
db_index=True适合单字段简单加速;Meta.indexes才能定义fields、name、condition(条件索引)等高级能力 - 避免对
TextField、JSONField直接建普通索引 —— 除非明确要用前缀索引或表达式索引,否则无效甚至拖慢写入
select_related 和 prefetch_related 用错反而更慢
这两个不是“加了就快”,而是有严格适用边界的预加载机制。滥用会导致数据膨胀、内存飙升,甚至比 N+1 更差。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
立即学习“Python免费学习笔记(深入)”;
-
select_related('author')只适用于正向 ForeignKey / OneToOneField,底层是INNER JOIN。若关联表数据量大(比如作者有 10 万条记录),一次 JOIN 可能返回百万行,远超业务需要 -
prefetch_related('articles')对反向关系或多对多有效,但它会发两条独立查询:一条主表,一条子表IN (id1,id2,...)。如果主表结果集太大(如 5000+ 条),IN列表会超长,MySQL 可能拒绝执行或退化为全表扫描 - 真正安全的做法是:先用
.values_list('id', flat=True)拿 ID 列表,再手动分批查子表(如每 500 个 ID 一批),避免单次查询失控 - 永远不要在
prefetch_related后再调用.count()或.exists()—— 它会忽略已 prefetch 的数据,重新发一次 COUNT 查询
聚合计算(如 GDD 积温)为什么总卡住,Subquery 怎么写才不扫全表
在百万级气象表上做跨地区、跨日期的聚合,Subquery + OuterRef 是高危写法。原始代码里 .values('commune_id').annotate(...).values('gdd')[:1] 会让数据库先 GROUP BY 再 LIMIT,无法利用索引下推。
- 必须确保子查询的
filter()条件字段(如date__range和commune_id)已被复合索引覆盖,否则子查询本身就会全表扫描 - 删掉冗余的
.values(...).annotate(...).values(...)链 —— 改用Sum( ... )直接聚合,避免中间分组开销 - 如果聚合逻辑复杂(如需逐日计算再求和),宁可拆成两步:先用
values('commune_id', 'date').annotate(daily_gdd=...)得到中间结果,再用 Pandas 或原生 SQL 汇总,也比硬扛 ORM 子查询强 - 测试时用
explain看执行计划:CommuneMeteo.objects.filter(...).explain(),确认是否用了索引、是否出现Using filesort或Using temporary
索引和预加载只是手段,真正的瓶颈往往藏在「你以为在查 100 行,其实数据库在扫 100 万行」这个认知差里。每次优化前,先看 explain 输出和真实 time,而不是凭经验改代码。

















