告别 N+1 地狱:深度理解Django中 select_related 与 prefetch_related
更多内容请见: 《Python Web项目集锦》 - 专栏介绍和目录
文章目录
- 第一章:地狱的起源——什么是 N+1 问题?
- 1.1 场景重现
- 1.2 数据库的灾难
- 1.3 ORM 的惰性求值是原罪吗?
- 第二章:第一把利剑——select_related(JOIN 的艺术)
- 2.1 基本用法
- 2.2 底层原理:SQL JOIN
- 2.3 为什么要限制在 OneToOne/ForeignKey?
- 2.4 链式查询与深度查找
- 第三章:第二把利剑——prefetch_related(分离的智慧)
- 3.1 适用场景与基本用法
- 3.2 底层原理:两次查询 + Python 拼接
- 3.3 为什么说它是“分离的智慧”?
- 第四章:深度对比——什么时候用哪个?
- 4.1 黄金法则
- 第五章:高阶技巧——Prefetch 对象与自定义查询
- 5.1 默认行为的问题
- 5.2 解决方案:`Prefetch` 对象
- 5.3 混合双打:select_related + prefetch_related
- 第六章:何时“不要”使用它们?
- 6.1 数据库负担 vs 应用内存负担
- 6.2 分页场景的陷阱
- 6.3 只读场景下的权衡
- 第七章:实战案例分析——电商订单系统
- 模型定义
- 需求
- 错误示范(N+1 爆炸)
- 初级优化(使用 prefetch_related)
- 终极优化(使用 Prefetch 对象 + 聚合)
- 第八章:总结——构建高性能思维
在现代 Web 开发中,性能往往决定了应用的成败。而对于基于 Django 的全栈开发者来说,导致性能崩盘最隐蔽、最常见的原因,莫过于臭名昭著的“N+1 查询问题”。
当你看着后台日志中数据库请求如瀑布般倾泻而下,或者页面加载条像蜗牛一样蠕动时,通常就是 N+1 问题在作祟。Django ORM 为了解决这一顽疾,提供了两把尚方宝剑:select_related和prefetch_related。
然而,很多开发者虽然知道这两个方法的存在,却往往知其然不知其所以然:为什么一个只能用在一对多,另一个用在多对多?它们的底层原理究竟有何不同?本文将深入 Django ORM 源码与 SQL 执行机制,带你彻底掌握这两个性能优化的核心利器,真正告别 N+1 地狱。
第一章:地狱的起源——什么是 N+1 问题?
1.1 场景重现
假设我们有一个经典的“图书-作者”模型。一位作者可以写多本书(一对多关系)。
fromdjango.dbimportmodelsclass