
关于
Django 性能代码审查。用于识别 N+1 查询、低效 ORM 用法、缺失索引和其他 Django 应用性能瓶颈。
name: django-perf-review description: Django 性能代码审查。当被要求"审查 Django 性能"、"查找 N+1 查询"、"优化 Django"、"检查 queryset 性能"、"数据库性能"、"Django ORM 问题"或审计 Django 代码的性能问题时使用。 allowed-tools: Read, Grep, Glob, Bash, Task license: LICENSE risk: unknown source: community
Django 性能审查
审查 Django 代码中已验证的性能问题。在报告之前先研究代码库以确认问题。只报告你能证明的问题。
适用场景
- 你需要一份专注于已验证的 ORM 和查询问题的 Django 性能审查。
- 代码可能存在 N+1 查询、无界查询集、缺失索引或其他数据库驱动的瓶颈。
- 你只需要可证明的性能发现,而非推测性的优化建议。
审查方法
- 先研究 - 追踪数据流,检查现有优化,验证数据量
- 报告前验证 - 模式匹配不等于验证
- 零发现是可接受的 - 不要为了显得彻底而制造问题
- 严重程度必须匹配影响 - 如果你发现自己在 CRITICAL 发现中写"轻微",那它就不是 CRITICAL。降级或跳过它。
影响分类
问题按影响组织。专注于 CRITICAL 和 HIGH - 这些在规模化时会造成真正的问题。
| 优先级 | 类别 | 影响 | |----------|----------|--------| | 1 | N+1 查询 | CRITICAL - 随数据量倍增,导致超时 | | 2 | 无界查询集 | CRITICAL - 内存耗尽,OOM 终止 | | 3 | 缺失索引 | HIGH - 大表全表扫描 | | 4 | 写入循环 | HIGH - 锁竞争,请求变慢 | | 5 | 低效模式 | LOW - 很少值得报告 |
优先级 1:N+1 查询(CRITICAL)
影响: 每个 N+1 增加 O(n) 次数据库往返。100 行 = 100 次额外查询。10,000 行 = 超时。
规则:预取循环中访问的关联数据
通过追踪验证:视图 → 查询集 → 模板/序列化器 → 循环访问
# PROBLEM: N+1 - each iteration queries profile
def user_list(request):
users = User.objects.all()
return render(request, 'users.html', {'users': users})
# Template:
# {% for user in users %}
# {{ user.profile.bio }} ← triggers query per user
# {% endfor %}
# SOLUTION: Prefetch in view
def user_list(request):
users = User.objects.select_related('profile')
return render(request, 'users.html', {'users': users})
规则:在序列化器中预取,而不仅仅是视图
DRF 序列化器访问关联字段时,如果查询集未优化会导致 N+1。
# PROBLEM: SerializerMethodField queries per object
class UserSerializer(serializers.ModelSerializer):
order_count = serializers.SerializerMethodField()
def get_order_count(self, obj):
return obj.orders.count() # ← query per user
# SOLUTION: Annotate in viewset, access in serializer
class UserViewSet(viewsets.ModelViewSet):
def get_queryset(self):
return User.objects.annotate(order_count=Count('orders'))
class UserSerializer(serializers.ModelSerializer):
order_count = serializers.IntegerField(read_only=True)
规则:在循环中查询的模型属性是危险的
# PROBLEM: Property triggers query when accessed
class User(models.Model):
@property
def recent_orders(self):
return self.orders.filter(created__gte=last_week)[:5]
# Used in template loop = N+1
# SOLUTION: Use Prefetch with custom queryset, or annotate
N+1 验证清单
- [ ] 追踪了从视图到模板/序列化器的数据流
- [ ] 确认关联字段在循环内被访问
- [ ] 搜索了代码库中现有的 select_related/prefetch_related
- [ ] 验证了表有显著的行数(1000+)
- [ ] 确认这是热路径(非管理后台,非罕见操作)
优先级 2:无界查询集(CRITICAL)
影响: 加载整个表会耗尽内存。大表导致 OOM 终止和 worker 重启。
规则:始终对列表端点分页
# PROBLEM: No pagination - loads all rows
class UserListView(ListView):
model = User
template_name = 'users.html'
# SOLUTION: Add pagination
class UserListView(ListView):
model = User
template_name = 'users.html'
paginate_by = 25
规则:大批量处理使用 iterator()
# PROBLEM: Loads all objects into memory at once
for user in User.objects.all():
process(user)
# SOLUTION: Stream with iterator()
for user in User.objects.iterator(chunk_size=1000):
process(user)
规则:永远不要对无界查询集调用 list()
# PROBLEM: Forces full evaluation into memory
all_users = list(User.objects.all())
# SOLUTION: Keep as queryset, slice if needed
users = User.objects.all()[:100]
无界查询集验证清单
- [ ] 表很大(10k+ 行)或会无限增长
- [ ] 没有分页类、paginate_by 或切片限制
- [ ] 确认端点在生产环境中被调用
兼容工具
Claude CodeCursor
标签
后端开发
