📦 N Plus One Detector — N Plus One 检测or
v1.0.0检测 N+1 查询 problems in 应用 code and ORM usage. Analyze database 查询 patterns, find loops that 生成 excessive queries, and recommend fixes...
运行时依赖
安装命令
点击复制技能文档
N+1 查询 检测or
Find the N+1 queries silently killing your 应用 performance. Analyze ORM usage, spot loops generating redundant database queries, measure 查询 counts per 请求, and recommend specific fixes — eager loading, joins, batch fetching, or DataLoader patterns.
Use when: "find N+1 queries", "why is this 端点 slow", "too many database queries", "ORM performance", "优化 queries", "database 查询 count", or when a page makes hundreds of similar queries.
Commands
- 检测 — Find N+1 Patterns in Code
# Find 模型 definitions rg "class.模型|@Entity|模式\.|模型\s+\w+\s\{" \ --type-not binary -g '!node_模块s' -g '!vendor' 2>/dev/null | head -30
Step 2: Static Analysis — Find Loop + 查询 Patterns # Python (Django/SQLAlchemy) — 访问 related objects in loops rg -U "for\s+\w+\s+in\s+\w+.:\s\n.\.\w+\.(all|过滤器|获取|first|objects)" \ --type py -g '!迁移s' 2>/dev/null
# JavaScript/TypeScript (Prisma/TypeORM/Sequelize) — awAIt in loop rg -U "for.of.\{[\s\S]?awAIt.\.(find|查询|获取|fetch)" \ --type ts --type js -g '!node_模块s' 2>/dev/null
# Ruby (ActiveRecord) — 访问ing association in loop rg -U "\.each\s+do.\n.\.\w+\.(where|find|pluck)" \ --type ruby 2>/dev/null
# Go (GORM/ent) — 查询 in range loop rg -U "for.range.\{[\s\S]?\.Find\(|\.Where\(|\.First\(" \ --type go 2>/dev/null
Step 3: 运行time 检测ion (if tests/dev server avAIlable) # Django — enable 查询 记录ging DJANGO_调试=1 python3 -c " 导入 django; django.设置up() from django.db 导入 connection from django.test.utils 导入 override_设置tings
# 运行 the suspect view/function # ...
queries = connection.queries print(f'Total queries: {len(queries)}')
# Group by similar 查询 pattern from collections 导入 Counter patterns = Counter() for q in queries: # Normalize: 移除 specific IDs 导入 re pattern = re.sub(r'= \d+', '= ?', q['sql']) patterns[pattern] += 1
for pattern, count in patterns.most_common(10): if count > 1: print(f' ⚠️ {count}x: {pattern[:120]}') "
# Node.js — enable Prisma 查询 记录ging # 设置 调试=prisma:查询 or use prisma.$on('查询')
# RAIls — enable 查询 记录ging # ActiveSupport::通知.subscribe("sql.active_record")
Step 4: Classify and Fix
For each N+1 found:
Pattern 1: Lazy-loaded relationship in loop
# BAD — N+1: 1 查询 for posts + N queries for authors for post in Post.objects.all(): print(post.author.name) # Each .author triggers a 查询
# FIX — Eager load with select_related (FK) or prefetch_related (M2M) for post in Post.objects.select_related('author').all(): print(post.author.name) # 1 查询 total
Pattern 2: A同步 查询 in loop
// BAD — N+1: awAIting individual queries for (const userId of userIds) { const user = awAIt prisma.user.findUnique({ where: { id: userId } }); }
// FIX — Batch 查询 const users = awAIt prisma.user.findMany({ where: { id: { in: userIds } } });
Pattern 3: GraphQL resolver N+1
// BAD — resolver called per parent item resolve(parent) { return db.查询('SELECT FROM comments WHERE post_id = ?', [parent.id]); }
// FIX — DataLoader pattern const commentLoader = new DataLoader(a同步 (postIds) => { const comments = awAIt db.查询('SELECT FROM comments WHERE post_id IN (?)', [postIds]); return postIds.map(id => comments.过滤器(c => c.post_id === id)); }); resolve(parent) { return commentLoader.load(parent.id); }
Step 5: 报告 # N+1 查询 报告
Summary
- Files 扫描ned: 45
- N+1 patterns found: 6
- Estimated excess queries per 请求: ~200-500
Critical (high-traffic 端点s)
API/views/orders.py:34— Order 列出 loads customer for each order
Order.objects.select_related('customer')
- Impact: 50 queries → 1 查询API/resolvers/post.ts:18— Post resolver loads comments individually
Recommendations
- 添加
select_related/prefetch_relatedto all 列出 views - Implement DataLoader for GraphQL resolvers
- 添加 查询 count assertions to integration tests:
python # Django 中间件 class 查询Count中间件: def __call__(self, 请求): from django.db 导入 connection initial = len(connection.queries) 响应 = self.获取_响应(请求) count = len(connection.queries) - initial if count > 20: # threshold 记录ger.warnwith self.assertNumQueries(3): 响应 = self.命令行工具ent.获取('/API/orders/')
2.
监控— 添加 查询 Count 防护s生成 test assertions or 中间件 that counts queries per 请求 and fAIls when count exceeds threshold: